Appearance
duploy.toml
The file lives at the root of the repository (or at the config path set on the project). Unknown keys are errors. Every error is reported with its key path and line, and an invalid file is not applied at all.
toml
version = 1
[services.<name>]
# …
[databases.<name>]
# …| Key | Type | Required | Notes |
|---|---|---|---|
version | integer | yes | Always 1 |
services | table | no | Service name → service |
databases | table | no | Database name → database |
Names of services and databases share one namespace and must match ^[a-z][a-z0-9-]{0,31}$.
Service
| Key | Type | Default | Notes |
|---|---|---|---|
type | "web" | "worker" | "cron" | "web" | |
source | table | — | Required. See source. |
port | integer | — | Required for web; not allowed for worker and cron |
replicas | integer ≥ 1 | 1 | Not for cron; 1 when the service has a volume |
command | array of strings | image CMD | |
domains | array of tables | [] | web only. See domains. |
health | table | by type | Not for cron. See health. |
schedule | string | — | cron only, required there. Five fields, UTC. |
timeout | duration | platform limit | cron only |
resources | table | plan defaults | See resources. |
inherit_env | string | — | Service whose resolved env this one starts from |
node | string | — | Pin to a named node |
env | table | {} | See env. |
volumes | table | {} | See volumes. |
files | table | {} | See files. |
network | table | — | See network. |
Durations are written 30s, 5m, 1h.
source
Exactly one of path, repo, image, image_of.
| Key | Type | Default | Notes |
|---|---|---|---|
path | string | "." | Build context, relative to the repository root |
repo | string | — | owner/name; build from another repository |
branch | string | default branch | Only with repo |
dockerfile | string | "Dockerfile" | Relative path |
build_args | table | {} | path/repo only. KEY = "value" or KEY = { secret = true }. Names match ^[A-Za-z_][A-Za-z0-9_]*$. |
image | string | — | Image reference; no build |
image_of | string | — | Another service's image; not itself, no chains |
path and repo sources need a project with a linked repository.
domains
| Key | Type | Default | Notes |
|---|---|---|---|
host | string | — | Required |
path | string | "/" | Must start with / |
strip_prefix | boolean | false | |
port | integer | service port | Container port for this domain |
protocol | "http1" | "h2c" | "http1" | h2c for gRPC backends |
The same host + path may appear only once across the project.
health
| Key | Type | Default | Notes |
|---|---|---|---|
http | string | — | Path for an HTTP GET; 2xx and 3xx pass |
cmd | array of strings | — | Command run in the container; exit 0 passes |
interval | duration | 10s | |
timeout | duration | 5s | |
retries | integer | 3 | Consecutive failures before unhealthy |
Exactly one of http and cmd. Without health, a web service is probed with a TCP connect to port and a worker is healthy after 5 seconds up.
resources
Also used by databases.
| Key | Type | Notes |
|---|---|---|
cpu | number | Cores |
memory | string | 512M, 2G, 256Mi, 1GB |
pids | integer | Process limit |
disk | string | Root filesystem quota; for databases, the data volume |
nofile | integer | Open file limit |
read_only | boolean | Read-only root filesystem, writable /tmp |
runtime | "runc" | "sysbox-runc" | "kata-clh" |
Missing keys come from the plan defaults. Values above the plan maximum are errors.
env
| Value | Meaning |
|---|---|
"text" | Literal |
{ secret = true } | Value set with duploy secret set |
{ from_database = "db", property = "url" } | url (default), host, port, user, password, database |
{ from_service = "api", key = "TOKEN" } | Variable of another service; key required |
volumes
| Value | Meaning |
|---|---|
"/data" | Mount path |
{ path = "/data", size = "10G", backup = { schedule = "0 4 * * *", retain = 7 } } | With quota and snapshots |
Paths are absolute, not /, and unique per service. retain is at least 1.
files
| Value | Meaning |
|---|---|
"content" | Inline content |
{ content = "…", mode = "0640" } | Inline content with a mode |
{ from = "config/app.yml" } | Repository file at the deployed commit |
Keys are absolute file paths in the container, outside every volume path. ${VAR} is resolved from the service env and platform variables; $${ escapes. Limits: 64 KiB per file, 16 files, 256 KiB in total.
network
| Key | Type | Notes |
|---|---|---|
egress | array of strings | Hosts, *.wildcards, IPs and CIDRs this service may reach; at most 64. Absent means unrestricted. |
Database
| Key | Type | Default | Notes |
|---|---|---|---|
engine | "postgres" | "mysql" | "redis" | — | Required |
version | string | — | Required. "18" or "18.2". Minimum: Postgres 18, MySQL 8, Redis 7. |
extensions | array of strings | [] | Postgres only: pgvector, postgis, pg_cron, timescaledb |
backup | table | — | schedule (five fields, UTC) and retain |
resources | table | plan defaults | See resources. |
node | string | — | Pin to a named node |
A complete example
toml
version = 1
[services.web]
source = { path = ".", dockerfile = "Dockerfile" }
port = 8080
replicas = 2
domains = [{ host = "shop.example.com" }]
health = { http = "/healthz", interval = "10s", retries = 6 }
resources = { cpu = 1.0, memory = "1G" }
[services.web.env]
DATABASE_URL = { from_database = "db" }
REDIS_URL = { from_database = "cache" }
SESSION_KEY = { secret = true }
[services.worker]
type = "worker"
source = { image_of = "web" }
inherit_env = "web"
command = ["bin/worker"]
[services.nightly]
type = "cron"
source = { image_of = "web" }
inherit_env = "web"
command = ["bin/report"]
schedule = "0 2 * * *"
timeout = "30m"
[databases.db]
engine = "postgres"
version = "18"
extensions = ["pgvector"]
backup = { schedule = "0 3 * * *", retain = 7 }
[databases.cache]
engine = "redis"
version = "7"