Skip to content

Compose labels

SimpleDeploy reads simpledeploy.* labels from your Docker Compose services to configure routing, access control, backups, alerts, and rate limiting.

services:
web:
image: myapp:latest
ports:
- "3000:3000"
labels:
simpledeploy.domain: "myapp.example.com"
simpledeploy.port: "3000"
simpledeploy.tls: "auto"
simpledeploy.backup.strategy: "postgres"
simpledeploy.backup.schedule: "0 2 * * *"
simpledeploy.backup.target: "s3"
simpledeploy.backup.retention: "7"
simpledeploy.alerts.cpu: ">80,5m"
simpledeploy.alerts.memory: ">90,5m"
simpledeploy.path.patterns: "/users/{id},/posts/{id}"
simpledeploy.ratelimit.requests: "100"
simpledeploy.ratelimit.window: "60s"
simpledeploy.ratelimit.by: "ip"
simpledeploy.ratelimit.burst: "20"
simpledeploy.access.allow: "10.0.0.0/8,203.0.113.5"
restart: unless-stopped
LabelRequiredDefaultDescription
simpledeploy.domainYes (for proxy)-Domain name for reverse proxy routing
simpledeploy.portNoFirst port mappingContainer port to proxy to
simpledeploy.tlsNoautoTLS mode: auto, custom, off

If simpledeploy.domain is not set, the app runs but has no proxy route (accessible only via host-mapped ports).

If simpledeploy.port is not set, SimpleDeploy uses the first port mapping it finds in the compose file.

Endpoint services no longer need to publish host ports to be reachable. SimpleDeploy auto-attaches them to a shared simpledeploy-public Docker network and reverse-proxies over that. ports: still works and, when present, takes precedence over the shared-network path.

ports: "8080:80" style mappings are rewritten at deploy time to 127.0.0.1:8080:80 so the published port is reachable only from the host itself. Caddy still proxies external traffic to the same upstream, but this prevents an unauthenticated attacker from reaching the app raw on :8080 and bypassing per-app simpledeploy.access.allow and simpledeploy.ratelimit.* controls.

Operator-explicit interface bindings ("0.0.0.0:8080:80", "127.0.0.1:9090:90", "[::1]:5432:5432") are preserved verbatim. To disable the rewrite globally, set SIMPLEDEPLOY_DISABLE_PORT_LOOPBACK=true.

Compose files are rejected at deploy time (and by the reconciler watcher on disk) if they declare any of the following container-escape vectors:

  • privileged: true
  • network_mode: host, pid: host, pid: container:*, pid: service:*
  • ipc: host, userns_mode: host, cgroup: host
  • Dangerous capabilities via cap_add: ALL, SYS_ADMIN, SYS_PTRACE, SYS_MODULE, SYS_RAWIO, SYS_BOOT, SYS_TIME, NET_ADMIN, NET_RAW, DAC_READ_SEARCH, DAC_OVERRIDE, BPF, PERFMON, MKNOD (with or without the CAP_ prefix).
  • security_opt: apparmor=unconfined, seccomp=unconfined, label=disable, systempaths=unconfined, no-new-privileges=false.
  • devices (any non-empty list).
  • volumes_from (any non-empty list).
  • Bind mounts of /etc, /proc, /sys, /dev, /var/run/docker.sock, /root, /var/lib/docker, /boot, /lib/modules, /run, /, or any path containing ...
  • Top-level volumes with driver_opts of the form type: none, o: bind, device: /host/path (the host-bind shim) when the device path falls in the bind-source deny list.

See IP access control for the full guide.

LabelDefaultDescription
simpledeploy.access.allow- (all traffic allowed)Comma-separated IPs and/or CIDRs
LabelDefaultDescription
simpledeploy.backup.strategyauto-detectedpostgres, mysql, mongo, redis, sqlite, or volume. Auto-detection picks a strategy from image keywords; set this label to force one explicitly.
simpledeploy.backup.schedule-Cron expression (5-field, e.g., 0 2 * * *)
simpledeploy.backup.target-s3 or local
simpledeploy.backup.retention7Number of backups to keep

Backup strategies and what they produce:

  • postgres: pg_dump -U $POSTGRES_USER -d $POSTGRES_DB via docker exec, gzipped. Reads user/db from container env.
  • mysql: mysqldump --all-databases -u root -p"$MYSQL_ROOT_PASSWORD" via docker exec, gzipped.
  • mongo: mongodump --archive --gzip --authenticationDatabase admin -u $MONGO_INITDB_ROOT_USERNAME -p $MONGO_INITDB_ROOT_PASSWORD. Restore uses --drop.
  • redis: triggers BGSAVE, waits for LASTSAVE to bump, then docker cp the RDB file out and gzips it.
  • sqlite: sqlite3 <path> .backup to produce a consistent snapshot. Requires the explicit file path in the backup config (paths: ["/data/app.db"]); auto-detect returns the mount dir but not the DB filename.
  • volume: tar -czf - on the configured paths. Good for files and as a fallback for DBs where a strategy-specific option doesn’t apply. Not safe to restore over a running DB.

The S3 target config (endpoint, bucket, credentials) is set via the API or UI, not compose labels.

LabelFormatDescription
simpledeploy.alerts.cpu>80,5mCPU threshold and duration
simpledeploy.alerts.memory>90,5mMemory threshold and duration

Format: {operator}{threshold},{duration} where operator is >, <, >=, <= and duration is like 5m, 10m.

These labels configure default alert rules auto-created when the app is deployed. Rules can be tuned or disabled via the API/UI.

See Rate limiting for the full guide, including key types and caveats.

LabelDefaultDescription
simpledeploy.ratelimit.requestsglobal default (200)Max requests per window
simpledeploy.ratelimit.windowglobal default (60s)Time window
simpledeploy.ratelimit.burstglobal default (50)Burst allowance above limit
simpledeploy.ratelimit.byglobal default (ip)Rate limit key
LabelDescription
simpledeploy.path.patternsComma-separated path patterns for normalization

Path patterns replace dynamic segments in URL paths for metrics grouping. Example: /users/{id},/posts/{id} normalizes /users/123 to /users/{id}.

If not set, SimpleDeploy auto-normalizes by replacing all-digit path segments with {id}.

LabelDefaultDescription
simpledeploy.registriesglobal configComma-separated registry names for this app

Override which registries are used when pulling images for this app:

labels:
simpledeploy.registries: "ghcr-org,my-ecr"

Special value none disables all registries (including global defaults):

labels:
simpledeploy.registries: "none"

If not set, the global registries list from the server config applies. Registry names reference credentials stored via simpledeploy registry add or the API.

Labels can be placed on any service in the compose file. SimpleDeploy merges labels across all services (first occurrence wins for duplicate keys).

For proxy routing, only one service per app should have simpledeploy.domain and simpledeploy.port.

For backups, place the backup labels on the service containing the data (e.g., the database service).

Each app is a subdirectory under the apps directory:

/etc/simpledeploy/apps/
+-- myapp/
| +-- docker-compose.yml
+-- api-service/
| +-- docker-compose.yml
+-- postgres/
+-- docker-compose.yml

The directory name becomes the app slug used in URLs and CLI commands.