The deployer reads your compose file itself and turns it into systemd units for rootless Podman (Quadlet). It supports the parts that make sense on a single server and rejects the rest with a clear message, the YAML path and the line, so nothing is silently dropped. The repository check (setup step ④) shows the result before anything is built.
A typical project
services:
app:
build: . # ./Dockerfile
environment:
DATABASE_URL: ${DATABASE_URL} # set in the deployer UI (Variables)
NODE_ENV: production
depends_on:
- redis
worker:
build: .
command: ["node", "worker.js"]
environment:
DATABASE_URL: ${DATABASE_URL}
redis:
image: docker.io/library/redis:7
With [web] service = "app" and port = 8080 in deployer.toml: app gets the
traffic and zero-downtime switches, worker is restarted on its new image after each successful
switch, and redis keeps running across deploys.
Supported
| Key | Notes |
|---|---|
image | Short names are expanded Docker-style (redis:7 → docker.io/library/redis:7). Pinning a version beats :latest. |
build (context, dockerfile, args, target) | Built on this server. The context must stay inside the repository. Build args end up in the image: no secrets there. |
environment, env_file | Merged; values you set in the deployer win over both. |
${VAR}, ${VAR:-default}, ${VAR:?message} | Filled from the project's variables, then from a .env next to the compose file. $$ is a literal $. |
volumes | Named volumes (kept across deploys, not backed up), and read-only bind mounts of files inside the repository (./config:/etc/app). Absolute host paths are rejected. |
depends_on | Including service_healthy (needs a healthcheck) and service_completed_successfully (for one-off jobs). |
healthcheck | Becomes a container health check; a service with one counts as started only once healthy. |
command, entrypoint, user, working_dir, hostname, init, read_only, tmpfs, shm_size, ulimits, stop_signal, stop_grace_period, extra_hosts, labels, cap_drop | Mapped one to one. |
cap_add | Only a small allow-list (e.g. NET_BIND_SERVICE, CHOWN, SETUID, SETGID). |
sysctls | Only net.*. |
mem_limit, cpus, pids_limit, deploy.resources.limits | Capped by the project's own limits. |
networks (with aliases) | Each project gets its own isolated network; services find each other by name. |
restart, profiles, x-* fields, YAML anchors | Supported. Profiles are enabled in deployer.toml. |
Ignored, with a warning
ports: nginx is the only public entry point; the deployer picks local ports itself.container_name: the deployer names containers (two copies of the web service run during a switch).expose,logging(logs always go to the server's journal), resourcereservations.
Rejected
privileged, network_mode, host pid/ipc/uts/userns_mode,
devices, volumes_from, links, secrets/configs (use the deployer's
variables, optionally as files), extends, include, scale or more than one replica,
security_opt, external volumes and networks, volume drivers, and any unknown key.
Good to know
- The web service must listen on
0.0.0.0inside the container and speak plain HTTP/1.1. - Containers run rootless; ports below 1024 inside the container are fine.
- Containers may connect to external hosts (your managed database); services on this server's host network are not reachable from them.
- All projects share one Podman user, and every name is prefixed with the project's name, so two projects
can both have a service called
app.