Creating a dynamic compose file
Dynamic compose is Runtipi’s app definition format for describing services, ports, volumes, and routing metadata. Apps are defined using a standard docker-compose.yml file with x-runtipi metadata.
Here is a minimal docker-compose.yml for a simple Nginx app:
services:
nginx:
image: nginx:1.25.3
x-runtipi:
is_main: true
internal_port: 80
x-runtipi:
schema_version: 2And that’s it — with just a few lines of config you can deploy an Nginx app with automatic Traefik routing.
Creating your first app
Create the docker-compose.yml
Create a docker-compose.yml file in your app folder. Start with the top-level x-runtipi metadata and an empty services block:
services:
# services go here
x-runtipi:
schema_version: 2The schema_version must be 2. Make sure dynamic_config is set to true in your app’s config.json.
Add a service
Add your first service with a Docker image:
services:
myapp:
image: myapp:latest
x-runtipi:
schema_version: 2Expose the web UI
If your service exposes a web UI, mark it as the main service with its port. Runtipi will automatically configure Traefik routing for this service:
services:
myapp:
image: myapp:latest
x-runtipi:
is_main: true
internal_port: 80
x-runtipi:
schema_version: 2Add volumes
Persist app data using standard Docker Compose volume syntax. Use ${APP_DATA_DIR} for app-specific storage:
services:
myapp:
image: myapp:latest
volumes:
- ${APP_DATA_DIR}/data:/data
x-runtipi:
is_main: true
internal_port: 80
x-runtipi:
schema_version: 2Add environment variables
Use standard Docker Compose environment variable syntax. You can reference variables from the app’s config.json form fields:
services:
myapp:
image: myapp:latest
volumes:
- ${APP_DATA_DIR}/data:/data
environment:
- FOO=bar
- PASSWORD=${MYAPP_PASSWORD}
x-runtipi:
is_main: true
internal_port: 80
x-runtipi:
schema_version: 2Add extra ports
For additional ports beyond the main web UI port (which Traefik handles), expose them directly:
services:
myapp:
image: myapp:latest
volumes:
- ${APP_DATA_DIR}/data:/data
environment:
- FOO=bar
- PASSWORD=${MYAPP_PASSWORD}
ports:
- "8080:8080/tcp"
- "25565:25565/udp"
x-runtipi:
is_main: true
internal_port: 80
x-runtipi:
schema_version: 2Add a healthcheck
Add container health monitoring using standard Docker Compose syntax:
services:
myapp:
image: myapp:latest
volumes:
- ${APP_DATA_DIR}/data:/data
environment:
- FOO=bar
- PASSWORD=${MYAPP_PASSWORD}
ports:
- "8080:8080/tcp"
- "25565:25565/udp"
healthcheck:
test: curl --fail http://localhost || exit 1
interval: 30s
timeout: 10s
retries: 3
x-runtipi:
is_main: true
internal_port: 80
x-runtipi:
schema_version: 2Add dependencies
If your app needs other services (like a database), add them and use depends_on:
services:
myapp:
image: myapp:latest
volumes:
- ${APP_DATA_DIR}/data:/data
environment:
- FOO=bar
- PASSWORD=${MYAPP_PASSWORD}
- DATABASE_URL=postgresql://postgres:${MYAPP_DB_PASS}@myapp-db:5432/myapp
ports:
- "8080:8080/tcp"
healthcheck:
test: curl --fail http://localhost || exit 1
interval: 30s
timeout: 10s
retries: 3
depends_on:
myapp-db:
condition: service_healthy
x-runtipi:
is_main: true
internal_port: 80
myapp-db:
image: postgres:16
volumes:
- ${APP_DATA_DIR}/db:/var/lib/postgresql/data
environment:
- POSTGRES_PASSWORD=${MYAPP_DB_PASS}
healthcheck:
test: pg_isready -U postgres
interval: 10s
timeout: 5s
retries: 5
x-runtipi:
schema_version: 2Additional options
Since this is standard Docker Compose, you can use any Docker Compose option: command, entrypoint, network_mode, cap_add, deploy, logging, devices, and more. See the Dynamic Compose Reference for Runtipi-specific options and the Docker Compose documentation for all available options.
For a complete reference of all x-runtipi metadata options and architecture
overrides, see the Dynamic Compose
Reference.
Legacy JSON Format
The section below covers the legacy docker-compose.json format. This format
is still supported for backward compatibility, but new apps should use
docker-compose.yml with x-runtipi metadata as shown above.
The legacy format uses a custom JSON structure. Here is the same Nginx example in JSON:
{
"schemaVersion": 2,
"services": [
{
"name": "nginx",
"image": "nginx",
"internalPort": 80,
"isMain": true
}
]
}Migrating from Schema v1 to v2
Schema version 2 introduces important structural changes to improve consistency and type safety. Here are the key differences:
Required schemaVersion field
All dynamic compose files must now include the schemaVersion field set to 2:
{
"schemaVersion": 2,
"services": [...]
}Environment variables structure change
v1 format (object):
{
"environment": {
"FOO": "bar",
"PASSWORD": "${MYAPP_PASSWORD}"
}
}v2 format (array of key-value pairs):
{
"environment": [
{
"key": "FOO",
"value": "bar"
},
{
"key": "PASSWORD",
"value": "${MYAPP_PASSWORD}"
}
]
}Automatic migration
Runtipi automatically migrates v1 schemas to v2 format when loading apps. However, it’s recommended to update your docker-compose.json files manually to take advantage of the improved validation and error messages.
Legacy JSON Validator
You can validate legacy JSON configurations using the validator below:
Docker Compose to Legacy JSON Converter
Converting an existing docker-compose.yml file to the legacy JSON format:
Docker Compose YAML
Dynamic Compose Output
The converter works for most cases, but please review the converted configuration to ensure it meets your needs. Some advanced features may not be fully supported.