Bring the familiar Docker Compose workflow to Incus containers. incus-compose implements the Compose specification for the Incus ecosystem, allowing you to define and run multi-container applications using the same docker-compose.yml files you already know.
Status
Early Development - This project is in its initial phase. APIs and behavior may change. Contributions and feedback are welcome!
It does “up, down and ps” those are well tested.
Compose projects get created with a incus project, storage pool volumes and as much bridge networks as you wish. Yes, it does also shift your Volumes transparently and it does bind mounts.
No specials included (caps and so on).
Why incus-compose?
Incus provides powerful system containers and virtual machines, but lacks the declarative multi-container orchestration that Docker Compose offers. This tool bridges that gap, letting you:
Use existing docker-compose.yml files with Incus containers
Leverage the superior security and isolation model of Incus
Run Docker/OCI images directly from registries like docker.io and ghcr.io
Manage complex multi-container applications with familiar commands
This is based on work done by @bketelsen
Some parts are replicated or copied from docker compose.
Im using AI to generate tests and to help me with reviews, real code is 90% hand written.
This is awesome news. I stopped mostly because I knew how much work it would be to get real health-check based orchestration going (don’t start wordpress until mysql is alive and serving on port 3306) — but this was before I started using any AI assistance. I hope you’re able to carry the torch and make something great!
Thank you for doing this work, I am really happy to see this continue.
I did a quick clone, followed by a go build -o incus-compose cmd/incus-compose/main.go and then found out API access through the unix socket is not currently supported. To be continued after I sort http API access w/ client certificates
11:04 ERR Error(s) during up url=https://localhost:8443 error=“failed to create image "ghcr.io/immich-app/postgres:18-vectorchord0.5.3": Failed remote image download: Failed getting remote image info: Image not found”
11:04 ERR Command returned error=“failed to create image "ghcr.io/immich-app/postgres:18-vectorchord0.5.3": Failed remote image download: Failed getting remote image info: Image not found”
Checking incus monitor output for more details gives:
location: none
metadata:
context:
name: mmich-app/postgres:18-vectorchord0.5.3
stderr: ‘Failed to run: skopeo --insecure-policy inspect docker://ghcr.io/mmich-app/postgres:18-vectorchord0.5.3:
exit status 1 (time=“2025-12-29T11:04:03+01:00” level=fatal msg=“Error parsing
image name "docker://ghcr.io/mmich-app/postgres:18-vectorchord0.5.3": Requesting
bearer token: received unexpected HTTP status: 403 Forbidden”)’
stdout: “”
level: debug
message: Error getting image alias
timestamp: “2025-12-29T11:04:03.960371876+01:00”
type: logging
Curiously skopeo is passed docker://ghcr.io/mmich-app/ (notice the missing i), which is a path that does not exist.
I have yet to find where the first character of the path gets removed, it’s rather weird. Not sure if this a local issue, a bug in incus-compose, a bug in incus, or something with skopeo.
Version information:
skopeo version 1.18.0
incus 6.20
incuscompose main@82c3ce34193bf06a43ba5cacbd5ad3f49476c7f7
With that change, and some edits to the docker-compose.yml to turn binds into volumes (still using the API) , incus-compose succesfully brought up Immich’s docker-compose config.
To get things to work in my local setup, I patched in a --pool-name cli parameter to change from the default pool (see below). Then, exploring Immich some more, I discovered that I would actually like to have the ability to map docker-compose volumes to incus storage pools. I could see a --volume volname:poolname or something, or alternatively some config file that defines such mappings.
@jochumdev do you have any such needs, or appetite for such a change?
I appreciate you taking the time to credit people, though I don’t feel I have done much I’ll let you decide.
My use case would be to use (supported) docker-compose.yml from various projects, but run them on Incus. For upgrades, it would be simpler if no modifications would need to be made to such files. So personally, I would have a slight preference for some kind of Incus-specific config file on the side, a bit like a .env file.
I don’t think I understand what you are after, but I’ll probably find out in the future .
Sure, I think I can find my way and send a patch or two if I run into something.
I’ve just noticed that I have zombie cgroups floating around that correspond to instances that incus-compose created. They persist when the instances are shut down and when incus is restarted. I am both unsure how this came about, or how to resolve this situation (edit: I can rmdir the cgroups manually).
Another observation that may more useful for direct improvement of incus-compose itself. Again going back to the immich docker-compose files; it seems the following bit of config for the immich-server component:
ports:
- '2283:2283'
gets translated in the following incus device config for the corresponding immich-server instance: