Skip to Content
DocumentationReferenceDynamic compose

Dynamic Compose Reference

Dynamic compose is Companion Hub’s JSON format for deploying an app: a simplified, custom version of the docker-compose.yml file that gives more control over how an app gets deployed. For example, you can choose to only use the reverse proxy or only use ports. Every app in the store ships a docker-compose.json in this format.

For a step-by-step guide on creating dynamic compose files, see Creating a dynamic compose file.

From docker-compose.json to a running stack

A dynamic compose file is not handed to Docker as-is. Hub validates it, folds in the override that matches the host’s CPU architecture, renders a compose project with Traefik labels on the isMain service, merges the values from the install form and any overrides you keep in user-config, and only then starts the containers:

Try it out

You can check the shape of your dynamic compose configuration using the validator below:

The validator checks your file against the general dynamic compose schema. It does not apply the rules Hub enforces when it installs an app, which are listed under What Hub refuses to install, and it does not recognize volumeName volumes. A file can pass here and still be refused at install.

Docker Compose to Dynamic Config Converter

Converting an existing docker-compose.yml file to Companion Hub’s dynamic compose format can be time-consuming and error-prone. We’ve created a tool to help you automatically convert your Docker Compose files to the dynamic compose format.

Docker Compose YAML

Dynamic Compose Output

The converter works for most cases, but review the converted configuration to ensure it meets your needs. Some advanced features may not be fully supported.

Configuration Options Reference

Schema Configuration

Top-level schema options:

OptionTypeDescriptionExample
schemaVersionnumberSchema version (must be 2)"schemaVersion": 2
servicesarrayArray of service configurationsSee service options below
overridesarrayArchitecture-specific overridesSee Architecture Overrides section

Basic Configuration

These are the most commonly used options when configuring a service:

OptionTypeDescriptionExample
namestring (required)The name of the service and container"name": "nginx"
imagestring (required)The Docker image to use"image": "nginx:latest"
commandstring | string[]The command to run in the container"command": "/my/app" or "command": ["npm", "start"]
environmentarrayEnvironment variablesSee example below

Example environment configuration:

"environment": [ {"key": "FOO", "value": "bar"}, {"key": "PORT", "value": 8080}, {"key": "ENABLE_FEATURE", "value": true} ]

Port Configuration

Options related to exposing ports:

OptionTypeDescriptionExample
internalPortnumberThe main port exposed by the container"internalPort": 80
addPortsarrayAdditional ports to exposeSee example below
restartstringRestart policy: no, always, unless-stopped, or on-failure"restart": "unless-stopped"
platformstringPin the service to a Docker platform"platform": "linux/amd64"

Example addPorts configuration:

"addPorts": [{ "containerPort": 8080, "hostPort": 8080, "tcp": true, "udp": false, "interface": "127.0.0.1" }]

Volume Configuration

Options for mounting volumes:

OptionTypeDescriptionExample
volumesarrayList of volume mappingsSee example below

Example volumes configuration:

"volumes": [{ "hostPath": "/host/path", "containerPath": "/container/path", "readOnly": false, "shared": false, "private": false, "bind": { "propagation": "rprivate" } }]

Each volume names exactly one source, either hostPath or volumeName:

  • hostPath is a bind mount from the host. Use ${APP_DATA_DIR} to stay inside the app’s own data folder.
  • volumeName is a Docker-managed named volume. Compose scopes the name to the app, so db cannot collide with another app’s db. A name must start with a letter or number.

Two optional flags change how a bind mount is created:

  • requiresPosixPermissions marks a bind mount that needs real file ownership, such as a database data directory. Where the host filesystem cannot carry ownership, such as a Windows-backed path, Hub mounts a named volume instead.
  • private marks a bind mount that only the app’s root-running service writes and that holds material other local accounts must not read. Hub creates it with owner-only permissions.

Volume bind propagation options: rprivate, private, rshared, shared, rslave, slave

Network Configuration

Options related to networking:

OptionTypeDescriptionExample
networkModestringCustom network mode"networkMode": "host"
addToMainNetworkbooleanAdd container to main network"addToMainNetwork": true
extraHostsstring[]Additional /etc/hosts entries"extraHosts": ["host1:ip1"]
hostnamestringContainer hostname"hostname": "mycontainer"
dnsstring | string[]Custom DNS servers"dns": "8.8.8.8" or "dns": ["8.8.8.8"]

Health Check Configuration

Options for container health monitoring:

OptionTypeDescriptionExample
healthCheckobjectContainer health check configurationSee example below

Example healthCheck configuration:

"healthCheck": { "test": "curl --fail http://localhost", "retries": 3, "interval": "30s", "timeout": "10s", "startInterval": "5s", "startPeriod": "30s" }

Resource Configuration

Options for container resource limits and deployment:

OptionTypeDescriptionExample
deployobjectResource limits and reservationsSee example below
ulimitsobjectResource limitsSee example below
shmSizestringSize of /dev/shm"shmSize": "2gb"

Example deploy configuration:

"deploy": { "resources": { "limits": { "cpus": "0.5", "memory": "512M", "pids": 100 }, "reservations": { "cpus": "0.25", "memory": "256M", "devices": [ { "capabilities": ["gpu"], "driver": "nvidia", "count": 1, "deviceIds": ["0"] } ] } } }

Example ulimits configuration:

"ulimits": { "nproc": {"soft": 10, "hard": 20}, "nofile": {"soft": 20, "hard": 30}, "core": 1024, "memlock": {"soft": 512, "hard": 1024} }

Note: ulimits can be specified as a number for simple limits or as an object with soft and hard values. Supported ulimit types: nproc, nofile, core, memlock.

Security Configuration

Options related to container security:

OptionTypeDescriptionExample
privilegedbooleanRun container with elevated privileges"privileged": true
capAddstring[]Add container capabilities"capAdd": ["NET_ADMIN"]
capDropstring[]Drop container capabilities"capDrop": ["NET_ADMIN"]
securityOptstring[]Security options"securityOpt": ["no-new-privileges"]
readOnlybooleanMount root filesystem as read only"readOnly": true
userstringUsername or UID"user": "1000:1000"

Advanced Configuration

Additional options for fine-tuning:

OptionTypeDescriptionExample
entrypointstring | string[]Container entrypoint"entrypoint": "./entrypoint.sh"
workingDirstringWorking directory inside container"workingDir": "/app"
ttybooleanAllocate a pseudo-TTY"tty": true
stdinOpenbooleanKeep STDIN open"stdinOpen": true
stopSignalstringSignal to stop the container"stopSignal": "SIGTERM"
stopGracePeriodstringTime to wait to stop container"stopGracePeriod": "10s"
pidstringPID namespace"pid": "host"
sysctlsobjectSysctl settings"sysctls": {"net.core.somaxconn": 1024}
loggingobjectLogging configurationSee example below
devicesstring[]Device mappings"devices": ["/dev/ttyUSB0:/dev/ttyUSB0"]
extraLabelsobjectAdditional container labels"extraLabels": {"com.example.key": "value"}

Example logging configuration:

"logging": { "driver": "json-file", "options": { "max-size": "10m" } }

Note: sysctls values must be numbers, and extraLabels values can be strings or booleans.

Dependencies

Options for managing service dependencies:

OptionTypeDescriptionExample
dependsOnobject | string[]Service dependenciesSee example below
isMainbooleanIndicates where traefik labels should be applied"isMain": true

Example dependsOn configurations:

Simple array format (waits for services to start):

"dependsOn": ["db", "cache"]

Object format with conditions:

"dependsOn": { "db": { "condition": "service_healthy" }, "cache": { "condition": "service_started" } }

Available conditions: service_healthy, service_started, service_completed_successfully

Architecture Overrides

Options for architecture-specific configurations:

OptionTypeDescriptionExample
overridesarrayArchitecture-specific service configurationsSee example below

The overrides option allows you to specify different configurations for specific architectures at the top level of your dynamic compose configuration. This is useful when you need to use different Docker images or settings for different CPU architectures (like arm64 vs amd64).

Example overrides configuration:

"overrides": [ { "architecture": "arm64", "services": [ { "name": "app", "image": "myapp:arm64-latest", "environment": [ {"key": "ARCHITECTURE", "value": "arm64"} ] } ] } ]

How Architecture Overrides Work

When Companion Hub starts a container, it automatically detects the current system architecture and applies any matching overrides from the overrides array. The system looks for overrides that match the current architecture, then merges those service-specific settings with the base service configuration.

Important aspects of the override system:

  • You must include the name field in each service in the overrides to match with the base service
  • Only specify the properties that need to be different for each architecture
  • Properties from overrides are deep-merged with the base service configurations
  • Array properties (like volumes or ports) in overrides completely replace the base arrays rather than being appended

Supported Architectures

Companion Hub supports the following architecture types for overrides:

  • amd64: Standard 64-bit x86 architecture (also known as x86_64)
  • arm64: 64-bit ARM architecture (also known as aarch64)

Complete Example

Here’s a complete example showing how to use architecture overrides:

{ "schemaVersion": 2, "services": [ { "name": "media-server", "image": "mediaserver:latest", "isMain": true, "internalPort": 8096, "volumes": [ { "hostPath": "${APP_DATA_DIR}/config", "containerPath": "/config" }, { "hostPath": "${APP_DATA_DIR}/media", "containerPath": "/media" } ], "environment": [{ "key": "TZ", "value": "UTC" }] } ], "overrides": [ { "architecture": "arm64", "services": [ { "name": "media-server", "image": "mediaserver:arm64-latest", "environment": [{ "key": "ARM_SPECIFIC_VAR", "value": "enabled" }] } ] }, { "architecture": "amd64", "services": [ { "name": "media-server", "image": "mediaserver:amd64-latest", "deploy": { "resources": { "reservations": { "devices": [ { "capabilities": ["gpu"] } ] } } } } ] } ] }

In this example:

  • The base configuration defines common settings for the media-server service
  • On arm64 systems, the service will use the ARM64-specific image and set an ARM-specific environment variable
  • On amd64 systems, the service will use the AMD64-specific image and enable GPU acceleration
  • The name “media-server” is included in each override to match with the base service

What Hub refuses to install

Hub checks every service against an app sandbox when it renders the compose file, including the values in overrides. An install that breaks these rules stops with an error that names the field. A few audited first-party and catalog apps are exempt from the rules they need. Apps from any other source, including an app you create yourself, are not.

  • Privileged mode and host namespaces. privileged: true, networkMode: host, networkMode: container:<name>, and pid: host are refused.
  • Capabilities. capAdd may only add Docker’s default capability set, such as CHOWN, NET_BIND_SERVICE, and SETUID. Anything else, such as NET_ADMIN or SYS_ADMIN, is refused.
  • Confinement. A securityOpt that switches confinement off, such as seccomp=unconfined, apparmor=unconfined, or any label= value, is refused.
  • Network mode. networkMode may be bridge, none, default, host, service:<name>, or container:<name>. Any other value would join a Docker network by name, and Hub never lets an app join one of its own networks.
  • Devices. Passing through a character device, such as a Zigbee dongle or /dev/dri, is fine. The /dev folder itself, block devices, and /dev/mem are refused.
  • Host paths. A hostPath may not be / or sit under, or contain, /etc, /var/run, /run, /var/lib/docker, /var/lib/containerd, /proc, /sys, /dev, /root, /boot, /usr, /bin, /sbin, /lib, or /lib64. The exceptions are /etc/localtime and /etc/timezone. A path with .. or . segments, a leading ~, or a $ expansion other than ${APP_DATA_DIR} is refused, because Hub cannot tell where it points.

Use ${APP_DATA_DIR} or a volumeName for storage, and ask for the smallest set of capabilities the app needs.

Note: Some of these options are advanced features that should be used with caution as they can affect container security and stability. Always refer to Docker documentation when using advanced options like privileged, capAdd, or securityOpt.

Tip: For most applications, you’ll only need the Basic Configuration and Port Configuration options. The other options are available for more complex use cases.

Last updated on