incus-compose handles environment variables differently than docker-compose for security and reproducibility reasons.
By default, incus-compose loads environment variables from:
.env file in the compose file's directory--env-fileThese .env files can reference OS environment variables for interpolation:
# .env
DB_PASSWORD=secret123
HOME_DIR=${HOME}
CURRENT_USER=${USER}
Only variables explicitly defined in .env files are passed to your compose
project. Your shell's environment (like PATH, EDITOR, etc.) is not
automatically included.
Values are not treated literally: a $ inside a value is itself interpolated
(PASSWORD=abc$def becomes abc, since $def resolves to nothing), and a
single-quoted value has no way to represent a literal '. Only double-quoting
with backslash-escaping (\\, \", \$) round-trips a value containing any of
these characters.
flowchart LR
ENV[".env plus<br/>--env-file files"] --> M[compose model]
OS["your shell environment<br/>PATH, EDITOR, ..."] -.->|"only where a .env line<br/>interpolates it"| ENV
OS -->|"--os-env / -E"| M
--os-env / -E FlagIf you need full docker-compose compatibility, use the --os-env flag:
incus-compose --os-env up
incus-compose -E up
This includes all OS environment variables directly, matching docker-compose behavior.
--os-env resolves before .env/--env-file in the merge order: when a key is
set by both, the shell's value wins and the .env value for that key is dropped
rather than overriding it.
# .env
DATABASE_URL=postgres://localhost/mydb
API_KEY=your-api-key
USER=${USER}
# compose.yaml
services:
app:
environment:
DATABASE_URL: ${DATABASE_URL}
API_KEY: ${API_KEY}
DEPLOYED_BY: ${USER}
incus-compose up
export DATABASE_URL=postgres://localhost/mydb
incus-compose --os-env up
| Method | Variables Available | Use Case |
|---|---|---|
| Default | .env files only (can interpolate OS vars) |
Production, CI/CD |
--os-env |
All OS environment variables | Quick testing, docker-compose compatibility |
Every global flag can be set via an environment variable. Flags given on the command line take precedence over environment variables.
flowchart TD
S([resolve a flag]) --> A{given on the command line?}
A -->|yes| USEF[use the flag]
A -->|no| B{"INCUS_COMPOSE_* variable set?"}
B -->|yes| USEE[use the variable]
B -->|no| USED[use the built-in default]
Every command-specific flag can be set too, scoped per command as
INCUS_COMPOSE_<COMMAND>_<FLAG> - e.g. --timeout on up is
INCUS_COMPOSE_UP_TIMEOUT, --timeout on down is
INCUS_COMPOSE_DOWN_TIMEOUT. Each command gets its own variable even when the
flag name is shared, so setting one never leaks into another command. See
Command Flags for the full list, or run
incus-compose <command> --help - every flag's env var is shown inline as
[$VAR_NAME].
Nine flags are the deliberate exception and have no environment variable, because a forgotten shell variable would silently make every future invocation destructive or a no-op instead of just changing cosmetic output:
| Flag | Command | Why |
|---|---|---|
--recreate |
up |
Would silently recreate containers on every up |
--project |
down |
Would silently delete the whole project |
--volumes |
down |
Would silently delete volumes |
--dry-run |
exec |
Would silently no-op every exec, breaking scripts |
--dry-run |
cp |
Would silently no-op every copy |
--dry-run |
port-forward |
Would silently no-op every forward |
--signal |
kill |
Takes only SIGKILL, which is already the default |
--yes |
backup restore |
Would silently skip the confirmation on a destructive restore |
--dry-run |
backup restore |
Would silently no-op every restore |
| Variable | Flag | Description |
|---|---|---|
INCUS_COMPOSE_FILE |
--file, -f |
Compose configuration files (comma-separated for multiple) |
INCUS_COMPOSE_PROJECT_NAME |
--project-name, -p |
Project name |
INCUS_COMPOSE_PROJECT_DIRECTORY |
--project-directory, -P |
Working directory |
INCUS_COMPOSE_ENV_FILE |
--env-file |
Alternative environment files (comma-separated for multiple) |
INCUS_COMPOSE_PROFILES |
--profile |
Profiles to enable (comma-separated for multiple) |
INCUS_COMPOSE_OS_ENV |
--os-env, -E |
Include OS environment variables for interpolation |
| Variable | Flag | Description |
|---|---|---|
INCUS_REMOTE |
--remote |
Incus remote name from CLI config (e.g., local, myserver) |
INCUS_COMPOSE_IMAGE_CACHE |
--image-cache |
Incus project used as image cache (INCUS_COMPOSE_IMAGE_CACHE, default: incus-compose-cache); set "" to disable caching and pull straight into the project, see CLI Reference |
INCUS_COMPOSE_STORAGE_POOL |
--storage-pool |
Default storage pool (default: detect) |
| Variable | Flag | Description |
|---|---|---|
INCUS_COMPOSE_ANSI |
--ansi |
Control ANSI output: never, always, auto (default: auto) |
INCUS_COMPOSE_DEBUG |
--debug |
Enable debug logging (true/1) |
INCUS_COMPOSE_TRACE |
--trace |
Per-event logging, which implies --debug; read by ic-healthd |
INCUS_COMPOSE_WORKERS |
--workers |
Number of concurrent workers (default: 4) |
NO_COLOR |
-- | Disable color output (no-color.org) |
--builder and --healthd-* are command flags (up, build, pull,
healthd up, healthd down), not global ones - see
Command Flags below for their per-command variable names.
The ic-healthd daemon reads a further set of INCUS_COMPOSE_HEALTHD_* variables
of its own, which incus-compose injects into the sidecar - see
The ic-healthd daemon below.
# Use a configured Incus remote
export INCUS_REMOTE=myserver
incus-compose up
# Set project defaults in your shell profile
export INCUS_COMPOSE_FILE=compose.yaml,compose.prod.yaml
export INCUS_COMPOSE_PROJECT_NAME=myapp
incus-compose up
# Debug with extra workers
INCUS_COMPOSE_DEBUG=1 INCUS_COMPOSE_WORKERS=20 incus-compose up
Every flag on every command below can be set via
INCUS_COMPOSE_<COMMAND>_<FLAG>, except the four listed under
CLI Configuration. Descriptions are abbreviated; run
incus-compose <command> --help for the full text and defaults.
| Command | Variable | Flag | Description |
|---|---|---|---|
up |
INCUS_COMPOSE_UP_NO_START |
--no-start |
Don't start containers after creating |
up |
INCUS_COMPOSE_UP_TIMEOUT |
--timeout |
Timeout for stopping/starting a service |
up |
INCUS_COMPOSE_UP_DEPENDENCY_TIMEOUT |
--dependency-timeout |
Max wait for service_healthy depends_on |
up/down |
INCUS_COMPOSE_SCALE |
--scale |
Scale SERVICE to NUM instances |
up |
INCUS_COMPOSE_UP_PULL |
--pull |
Pull policy |
up |
INCUS_COMPOSE_UP_BUILD |
--build |
Build images before starting |
up |
INCUS_COMPOSE_UP_BUILDER |
--builder |
Preferred builder binary or path |
up |
INCUS_COMPOSE_UP_NO_BUILD |
--no-build |
Do not build images even if missing |
up |
INCUS_COMPOSE_UP_NO_DEPS |
--no-deps |
Don't start linked services |
up |
INCUS_COMPOSE_UP_DETACH |
--detach, -d |
Run containers in the background |
up/down |
INCUS_COMPOSE_NO_HEALTHD |
--no-healthd |
Don't create the healthd sidecar |
up |
INCUS_COMPOSE_EXTERNAL_HEALTHD |
--external-healthd |
Use healthd but don't create/look it up |
up |
INCUS_COMPOSE_HEALTHD_IMAGE |
--healthd-image |
Healthd OCI image |
up |
INCUS_COMPOSE_HEALTHD_BINARY |
--healthd-binary |
Local ic-healthd binary path |
up |
INCUS_COMPOSE_HEALTHD_INCUS |
--healthd-incus |
Incus API URL for the sidecar |
up |
INCUS_COMPOSE_HEALTHD_NETWORK |
--healthd-network |
Network for the sidecar |
up |
INCUS_COMPOSE_HEALTHD_SCOPE |
--healthd-scope |
global or project |
down |
INCUS_COMPOSE_DOWN_RMI |
--rmi |
Remove images used by services |
down |
INCUS_COMPOSE_DOWN_IMAGES |
--images |
Remove known images from the project |
down |
INCUS_COMPOSE_DOWN_TIMEOUT |
--timeout |
Timeout for stopping |
down |
INCUS_COMPOSE_DOWN_NO_DEPS |
--no-deps |
Don't stop linked services |
down |
INCUS_COMPOSE_EXTERNAL_HEALTHD |
--external-healthd |
Use healthd but don't look it up |
down |
INCUS_COMPOSE_DOWN_NO_NETWORKS |
--no-networks |
Don't touch networks |
start |
INCUS_COMPOSE_START_TIMEOUT |
--timeout |
Timeout for starting |
start |
INCUS_COMPOSE_START_WITH_DEPS |
--with-deps |
Also start linked services |
stop |
INCUS_COMPOSE_STOP_TIMEOUT |
--timeout |
Timeout for stopping |
stop |
INCUS_COMPOSE_STOP_WITH_DEPS |
--with-deps |
Also stop linked services |
kill |
INCUS_COMPOSE_STOP_WITH_DEPS |
--with-deps |
Also kill linked services |
pause |
INCUS_COMPOSE_PAUSE_WITH_DEPS |
--with-deps |
Also pause linked services |
unpause |
INCUS_COMPOSE_UNPAUSE_WITH_DEPS |
--with-deps |
Also unpause linked services |
restart |
INCUS_COMPOSE_RESTART_TIMEOUT |
--timeout |
Timeout for stopping and starting |
restart |
INCUS_COMPOSE_RESTART_WITH_DEPS |
--with-deps |
Also restart linked services |
kill is the one command that reads another's variables: it is stop without
the graceful shutdown, so it takes INCUS_COMPOSE_STOP_WITH_DEPS rather than a
pair of its own.
up --recreate and down --project/--volumes have no variable - see the
exceptions table above.
| Command | Variable | Flag | Description |
|---|---|---|---|
build |
INCUS_COMPOSE_BUILD_NO_CACHE |
--no-cache |
Do not use a cache when building |
build |
INCUS_COMPOSE_BUILD_PULL |
--pull |
Pull policy |
build |
INCUS_COMPOSE_BUILD_BUILDER |
--builder |
Preferred builder binary or path |
pull |
INCUS_COMPOSE_PULL_IGNORE_BUILDABLE |
--ignore-buildable |
Ignore images that can be built |
pull |
INCUS_COMPOSE_PULL_IGNORE_PULL_FAILURES |
--ignore-pull-failures |
Pull what it can, ignore failures |
pull |
INCUS_COMPOSE_PULL_INCLUDE_DEPS |
--include-deps |
Also pull linked services |
pull |
INCUS_COMPOSE_PULL_POLICY |
--policy |
Pull policy |
pull |
INCUS_COMPOSE_NO_HEALTHD |
--no-healthd |
Don't pull the healthd sidecar |
pull |
INCUS_COMPOSE_HEALTHD_IMAGE |
--healthd-image |
Healthd OCI image |
| Command | Variable | Flag | Description |
|---|---|---|---|
| all | INCUS_COMPOSE_BACKUP_POOL |
--pool |
Storage pool for backup volumes |
backup list |
INCUS_COMPOSE_BACKUP_LIST_FORMAT |
--format |
Output format |
backup verify |
INCUS_COMPOSE_BACKUP_VERIFY_FORMAT |
--format |
Output format |
backup restore |
INCUS_COMPOSE_BACKUP_RESTORE_VOLUME |
--volume |
Restore only these volumes |
backup delete |
INCUS_COMPOSE_BACKUP_DELETE_KEEP_LAST |
--keep-last |
Delete every backup but the newest N |
| Variable | Flag | Description |
|---|---|---|
INCUS_COMPOSE_CONFIG_FORMAT |
--format |
Output format: yaml or json |
INCUS_COMPOSE_CONFIG_SERVICES |
--services |
Print the service names, one per line |
INCUS_COMPOSE_CONFIG_VOLUMES |
--volumes |
Print the volume names, one per line |
INCUS_COMPOSE_CONFIG_NETWORKS |
--networks |
Print the network names, one per line |
INCUS_COMPOSE_CONFIG_PROFILES |
--profiles |
Print the profile names, one per line |
INCUS_COMPOSE_CONFIG_QUIET |
--quiet, -q |
Only validate, don't print anything |
INCUS_COMPOSE_CONFIG_IMAGES |
--images |
Print the image names, one per line |
INCUS_COMPOSE_CONFIG_ENVIRONMENT |
--environment |
Print environment used for interpolation |
INCUS_COMPOSE_CONFIG_VARIABLES |
--variables |
Print model variables and default values |
INCUS_COMPOSE_CONFIG_OUTPUT |
--output, -o |
Save to file (default: stdout) |
| Command | Variable | Flag | Description |
|---|---|---|---|
list |
INCUS_COMPOSE_LIST_FORMAT |
--format |
Output format: table, yaml or json |
list |
INCUS_COMPOSE_NO_HEALTHD |
--no-healthd |
Don't list the healthd sidecar |
ps |
INCUS_COMPOSE_PS_ALL |
--all, -a |
Show all containers, including stopped |
ps |
INCUS_COMPOSE_PS_QUIET |
--quiet, -q |
Only display Incus instance names |
ps |
INCUS_COMPOSE_PS_SERVICES |
--services |
Display services instead of instances |
ps |
INCUS_COMPOSE_PS_FORMAT |
--format |
Output format: table or json |
ps |
INCUS_COMPOSE_PS_WITH_DEPS |
--with-deps |
Also list linked services |
| Command | Variable | Flag | Description |
|---|---|---|---|
run |
INCUS_COMPOSE_RUN_RM |
--rm |
Remove the instance after the command exits |
run |
INCUS_COMPOSE_RUN_DETACH |
--detach, -d |
Print the instance name and return |
run |
INCUS_COMPOSE_RUN_ENV |
--env, -e |
Set environment variables (KEY=VALUE) |
run |
INCUS_COMPOSE_RUN_LABEL |
--label, -l |
Add a label (KEY=VALUE) |
run |
INCUS_COMPOSE_RUN_VOLUME |
--volume, -v |
Bind mount a volume |
run |
INCUS_COMPOSE_RUN_PUBLISH |
--publish, -p |
Publish a port |
run |
INCUS_COMPOSE_RUN_SERVICE_PORTS |
--service-ports, -P |
Keep the ports the service declares |
run |
INCUS_COMPOSE_RUN_ENTRYPOINT |
--entrypoint |
Override the image entrypoint |
run |
INCUS_COMPOSE_RUN_USER |
--user, -u |
Run as this user |
run |
INCUS_COMPOSE_RUN_GROUP |
--group |
Run as this group |
run |
INCUS_COMPOSE_RUN_WORKDIR |
--workdir, -w |
Working directory for the command |
run |
INCUS_COMPOSE_RUN_NAME |
--name |
Name for the one-off instance |
run |
INCUS_COMPOSE_RUN_NO_TTY |
--no-tty, -T |
Disable pseudo-TTY allocation |
run |
INCUS_COMPOSE_RUN_NO_DEPS |
--no-deps |
Don't start the services this one depends on |
run |
INCUS_COMPOSE_RUN_BUILD |
--build |
Build the image before running |
run |
INCUS_COMPOSE_RUN_NO_BUILD |
--no-build |
Never build |
run |
INCUS_COMPOSE_RUN_BUILDER |
--builder |
Preferred builder |
run |
INCUS_COMPOSE_RUN_PULL |
--pull |
always / missing / never |
run, pull, up |
INCUS_COMPOSE_INIT_IMAGE |
--init |
Image the blocking helper comes from |
run |
INCUS_COMPOSE_RUN_TIMEOUT |
--timeout |
Timeout for creating and stopping the one-off |
| Command | Variable | Flag | Description |
|---|---|---|---|
logs |
INCUS_COMPOSE_LOGS_FOLLOW |
--follow, -f |
Follow log output |
exec |
INCUS_COMPOSE_EXEC_DETACH |
--detach, -d |
Run command in the background |
exec |
INCUS_COMPOSE_EXEC_ENV |
--env, -e |
Set environment variables (KEY=VALUE) |
exec |
INCUS_COMPOSE_EXEC_INDEX |
--index |
Replica index if service is scaled |
exec |
INCUS_COMPOSE_EXEC_NO_TTY |
--no-tty, -T |
Disable pseudo-TTY allocation |
exec |
INCUS_COMPOSE_EXEC_PRIVILEGED |
--privileged |
Accepted but not implemented |
exec |
INCUS_COMPOSE_EXEC_USER |
--user, -u |
Run the command as this user |
exec |
INCUS_COMPOSE_EXEC_GROUP |
--group, -g |
Run the command as this group |
exec |
INCUS_COMPOSE_EXEC_WORKDIR |
--workdir, -w |
Path to workdir directory |
cp |
INCUS_COMPOSE_CP_INDEX |
--index |
Replica index if service is scaled |
cp |
INCUS_COMPOSE_CP_ARCHIVE |
--archive, -a |
Keep the source's uid/gid |
cp |
INCUS_COMPOSE_CP_FOLLOW_LINK |
--follow-link, -L |
Always follow symlinks in SRC_PATH |
top |
INCUS_COMPOSE_TOP_COLUMNS |
--columns, -c |
Columns to display |
top |
INCUS_COMPOSE_TOP_FORMAT |
--format |
Output format: table or compact |
top |
INCUS_COMPOSE_TOP_REFRESH |
--refresh |
Refresh delay in seconds |
events |
INCUS_COMPOSE_EVENTS_TYPE |
--type, -t |
Event types to listen for |
events |
INCUS_COMPOSE_EVENTS_FORMAT |
--format |
Output format: pretty, yaml or json |
events |
INCUS_COMPOSE_EVENTS_JSON |
--json |
Short for --format=json |
port |
INCUS_COMPOSE_PORT_INDEX |
--index |
Replica index if service is scaled |
port |
INCUS_COMPOSE_PORT_PROTOCOL |
--protocol |
Protocol of the port, tcp or udp |
port-forward |
INCUS_COMPOSE_PORT_FORWARD_INDEX |
--index |
Replica index if service is scaled |
self-update |
INCUS_COMPOSE_SELF_UPDATE_DRAFT |
--draft |
Also consider draft releases |
self-update |
INCUS_COMPOSE_SELF_UPDATE_PRE_RELEASE |
--pre-release |
Also consider pre-releases |
exec --dry-run, cp --dry-run, port-forward --dry-run and
backup restore --yes/--dry-run have no variable - see the exceptions table
above.
These are the incus-compose healthd <subcommand> management commands (see
CLI Reference), distinct from up's own --healthd-*
flags above.
| Command | Variable | Flag | Description |
|---|---|---|---|
healthd up |
INCUS_COMPOSE_HEALTHD_IMAGE |
--image |
Healthd OCI image |
healthd up |
INCUS_COMPOSE_HEALTHD_BINARY |
--binary |
Local ic-healthd binary path |
healthd up |
INCUS_COMPOSE_HEALTHD_INCUS |
--incus |
Incus API URL for the sidecar |
healthd up |
INCUS_COMPOSE_HEALTHD_NETWORK |
--network |
Network for the sidecar (project scope) |
healthd up |
INCUS_COMPOSE_HEALTHD_SCOPE |
--scope |
global or project |
healthd up |
INCUS_COMPOSE_HEALTHD_PULL |
--pull |
Pull policy |
healthd up |
INCUS_COMPOSE_HEALTHD_TIMEOUT |
--timeout |
Timeout for stopping |
healthd down |
INCUS_COMPOSE_HEALTHD_IMAGE |
--image |
Healthd OCI image |
healthd down |
INCUS_COMPOSE_HEALTHD_DOWN_FORCE |
--force |
Stop a shared daemon without asking |
healthd down |
INCUS_COMPOSE_HEALTHD_TIMEOUT |
--timeout |
Timeout for stopping |
healthd logs |
INCUS_COMPOSE_HEALTHD_LOGS_FOLLOW |
--follow, -f |
Follow log output |
healthd restart |
INCUS_COMPOSE_HEALTHD_RESTART_TIMEOUT |
--timeout |
Timeout for stopping |
These are read by the ic-healthd binary itself, not by incus-compose. In the
normal flow incus-compose sets them on the sidecar and you never touch them;
they matter when you run the daemon yourself (see
ic-healthd Internals - Running the daemon directly).
| Variable | Flag | Default | Description |
|---|---|---|---|
INCUS_COMPOSE_HEALTHD_INCUS |
--incus |
- | Incus API URL the daemon connects to |
INCUS_COMPOSE_HEALTHD_TOKEN |
--token |
- | One-time trust token used to register its cert |
INCUS_COMPOSE_HEALTHD_PROJECTS |
--project |
- | Projects to watch, comma-separated; see below |
INCUS_COMPOSE_HEALTHD_PROJECT_MARKER |
--project-marker |
user.healthcheck.scope=global |
Project config KEY=VALUE consulted when _PROJECTS is unset |
INCUS_COMPOSE_HEALTHD_OWN_PROJECT |
--own-project |
- | Project the daemon's own container runs in |
INCUS_COMPOSE_HEALTHD_OWN_NAME |
--own-name |
- | The daemon's own instance name; empty skips itself |
INCUS_COMPOSE_HEALTHD_DATA_DIR |
--data-dir |
/var/lib/ic-healthd |
Persistent directory for the generated cert/key |
INCUS_COMPOSE_HEALTHD_SECRETS_DIR |
--secrets-dir |
/run/secrets |
Tmpfs directory holding the token file |
INCUS_COMPOSE_HEALTHD_DEBUG |
--debug |
false |
Verbose logging |
INCUS_COMPOSE_HEALTHD_TRACE |
--trace |
false |
Per-event logging, which implies --debug |
_PROJECTS may be left unset, in which case the daemon watches every project it
can see whose config matches _PROJECT_MARKER - by default
user.healthcheck.scope=global, which is what incus-compose stamps on the
projects it hands to the shared daemon. A bare key means KEY=true. Set
_PROJECTS explicitly and it is used verbatim, marker ignored. Either way the
daemon's trust token bounds what it can see at all.
Note that INCUS_COMPOSE_HEALTHD_INCUS appears twice on this page with two
different readers: on up and healthd up it tells incus-compose what
endpoint to configure the sidecar with, and here it is what the daemon dials.
They agree in the normal flow because the former is how the latter gets set.