Docker Compose
The docker-compose orchestrator manages a set of containers defined by a Docker Compose file.
Getting Started
1. Write a Compose File
Create a standard docker-compose.yml. Use ${RUGIX_APP_DATA_DIR} for persistent data that should survive across updates:
services:
mosquitto:
image: eclipse-mosquitto:2.1.2-alpine
restart: unless-stopped
ports:
- "127.0.0.1:1883:1883"
volumes:
- ./mosquitto.conf:/mosquitto/config/mosquitto.conf:ro
- ${RUGIX_APP_DATA_DIR}/mosquitto:/mosquitto/data2. Pack the Bundle
Use rugix-bundler to package the application into an app bundle:
rugix-bundler apps pack docker-compose \
--platform linux/arm64 \
--app my-app \
--include mosquitto.conf \
docker-compose.yml \
my-app-v1.rugixb
This command:
- Generates the
app.tomlmanifest withorchestrator = "docker-compose". - Copies registry images referenced in the compose file for the specified platform.
- Builds services with Compose
build:entries using Podman by default. - Saves each bundled image as a tarball so the bundle is fully self-contained.
- Rewrites the packaged Compose file to Rugix-owned bundle-local image tags with
pull_policy: never. - Packages everything into a Rugix Bundle.
Image bundling requires skopeo on the build machine. Services with Compose
build: entries additionally require the selected builder (podman by default,
or docker when using --builder docker).
Options:
--pullonly affects services with Composebuild:entries. It passes--pullto Podman/Docker so DockerfileFROMimages are refreshed before building the service image. Services that only declareimage:are copied by Skopeo and are unaffected.--builderselects the builder for Composebuild:entries. The default ispodman; use--builder dockerto build with Docker instead.--platformtargets the device architecture (e.g.,linux/arm64,linux/amd64).--includeadds extra files or directories to the bundle.--componentsadds component TOML or JSON files and directories to the bundle’s compatibility metadata.--health-check-timeoutwrites the Docker Compose health-check timeout intoapp.toml. The default is 120 seconds; use0to disable waiting.--config-schemaand--config-defaultinclude the app’s optional JSON configuration contract.--config-env NAME=JSON_POINTERprojects a selected JSON scalar into the Compose interpolation environment. The option may be repeated.--metadata-fileincludes an app metadata JSON file asapp-meta.json.--disable-image-bundlingskips bundling container images, useful when devices pull images from a registry at runtime.--disable-pinningkeeps the Composeimage:references as-is instead of rewriting them to Rugix-owned content tags. Images are still bundled under those original references.
By default, services with build: entries are built locally and services with
only image: entries are copied from a registry. To bundle an image that
already exists in local container storage, add an x-rugix extension to the
service:
services:
worker:
image: localhost/example/worker:dev
x-rugix:
image:
source: containers-storage # or docker-daemon
Use x-rugix.image.ref only when the local source reference differs from the
Compose image: reference.
Bundle a Prebuilt Image Archive
A docker-archive source packages an image created by docker save or Nix’s
dockerTools without loading it into a local container daemon:
services:
worker:
image: example/worker:1
x-rugix:
image:
source: docker-archive
ref: images/worker.tar.gz
The archive path is required and resolves relative to the Compose file. Absolute
paths are also accepted. Use an archive containing one image and a path without
colons. Skopeo handles compressed archives. Normal bundling rewrites the image
reference to a Rugix-owned content tag and includes the image as a separate
payload. With --disable-pinning, provide a Compose image tag to retain.
For Nix builds, the Rugix flake exports lib.mkComposeBundle. It accepts Compose
configuration and one image archive derivation per service, supplies Skopeo and
Bundler, and produces the bundle plus its verification hash. See the
Nix helper documentation
for an example and the argument reference.
3. Install on the Device
rugix-ctrl apps install --bundle-hash HASH my-app-v1.rugixb
This extracts the payloads, loads the container images with docker image load, and runs docker compose up -d --wait.
4. Update
Update the compose file or image tags, pack a new bundle, and install it:
rugix-ctrl apps install --bundle-hash HASH my-app-v2.rugixb
The old generation is deactivated (docker compose down), then the new generation is activated (docker compose up -d --wait). The previous generation remains on disk for rollback.
Generation Directory
The generation directory must contain a docker-compose.yml file. It may also contain:
- An
images/directory with Docker image tarballs (.tarfiles) that are loaded withdocker image loadduring activation.
When using rugix-bundler apps pack docker-compose, this structure is created automatically.
Lifecycle Operations
The orchestrator drives Docker directly via docker compose and the Docker daemon. No service manager is involved.
| Operation | Implementation |
|---|---|
| activate | Loads all images, then runs docker compose up -d --wait (see Health Checks). |
| status | Checks docker compose ps --format json output. |
| deactivate | Runs docker compose down. |
| start | Runs docker compose up -d --wait (without image loading). |
| stop | Runs docker compose stop. |
Boot behavior after stop: docker compose stop stops the containers but does not remove them. Whether Docker restarts them on boot depends on the restart policy in the compose file. With restart: always, Docker restarts the containers automatically. With restart: unless-stopped, Docker remembers that the containers were explicitly stopped and does not restart them.
Health Checks
During activation, docker compose up is called with --wait, which blocks until all containers with a healthcheck are healthy. If any container fails its health check, activation fails and the previous generation is automatically rolled back. Containers without a healthcheck are considered ready as soon as they are running.
The health check timeout defaults to 120 seconds and can be configured in app.toml:
orchestrator = "docker-compose"
[health-check]
timeout = 120Set timeout to 0 to disable waiting for health checks entirely (activation succeeds as soon as the containers are started).
Environment Variables
The following environment variables are available in the docker-compose.yml:
| Variable | Description |
|---|---|
RUGIX_APP_DATA_DIR | Absolute path to the app’s persistent data directory. |
RUGIX_APP_CONFIG_PATH | Absolute path to the effective JSON configuration, when one exists. |
Projecting Configuration Values
Environment projection is specific to the Docker Compose orchestrator. Declare each variable explicitly when packing the app, using an RFC 6901 JSON Pointer:
rugix-bundler apps pack docker-compose \
--app my-app \
--config-schema config.schema.json \
--config-env SERVER_URL=/serverUrl \
docker-compose.yml \
my-app.rugixb
Then use normal Compose interpolation:
services:
server:
image: example/server
environment:
SERVER_URL: ${SERVER_URL:-https://default.example.com}Only strings, numbers, and booleans can be projected. A missing or null value leaves the variable unset, allowing the Compose default to take effect. Objects and arrays cause activation to fail. Names beginning with RUGIX_ are reserved.
NixOS Integration
The Rugix flake exports nixosModules.rugix for app recovery and daemon services.
Enabling services.rugix.apps.dockerCompose.enable also enables Rugix and Docker
and declares the Compose runtime component. See the
NixOS module guide
for a configuration example.