“Bring Docker Compose workflows to Incus!”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.
Why incus-compose?
Incus provides powerful system containers and virtual machines with superior security and isolation, but lacks the declarative multi-container orchestration that Docker Compose offers. This tool bridges that gap:
Use existing docker-compose.yml files with Incus containers
Leverage Incus’s native OCI registry support for image pulling
Run Docker/OCI images directly from registries
Manage complex multi-container applications with familiar commands
Benefit from Incus’s resource efficiency and security model
Quick Links
Getting Started - Install and run your first compose project
# Build from source
git clone https://gitlab.com/r3j0/incus-compose
cd incus-compose
just build
# Or install directly
go install gitlab.com/r3j0/incus-compose/cmd/incus-compose@latest
Usage
# Create a compose.yaml
cat > compose.yaml <<EOF
services:
web:
image: docker.io/nginx:alpine
ports:
- "8080:80"
volumes:
- web-data:/usr/share/nginx/html
volumes:
web-data:
EOF
# Start services
incus-compose up
# View logs
incus-compose logs -f
# List running services
incus-compose list
# Stop and remove
incus-compose down
This project builds on work by @bketelsen.
Some components are adapted from docker compose.
This project uses AI tools as development aids (drafting, iteration, reviews, tests, and documentation).
Architecture, constraints, and final code decisions are owned by the human committers.
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: