An app backend is described by a Compose file in the package, usually backend/compose.yaml. The box accepts only a subset of Compose, defined by the allowlist sdk/schema/compose.v1.rules.json (published as @pithyx/sdk/schema/compose.v1.rules.json). The box and pithyx validate read the same file, so they judge a backend alike. Every key that is not listed below is refused with app.compose.key_unsupported. The tables are generated from the rules file each time the site is built; how a backend is built is explained in Backends.
The box rewrites the file before it runs it: it sets the project name, the network, the user, logging, mounts, limits and labels itself, so those keys are refused here. The rules are a stable contract: allowing a key or loosening a limit is a minor change, anything else a major one.
- The file is YAML without directives, anchors, aliases, tags, merge keys or duplicate keys (
app.compose.yaml_invalid), at most the size, depth and number of entries in Limits.
- Strings cannot contain control characters.
$ starts an interpolation, which is refused; write $$ for a literal $.
- Every image is pinned by digest (
name@sha256:<64 hex>, a tag may stand before the digest).
- A value that breaks a rule is
app.compose.value_invalid with a reason: type, format, range, required, duplicate, reserved, interpolation, unpinned, host_path, anonymous, no_value, cycle or image_and_build.
- Names used across files must exist: services in
depends_on, the manifest’s endpoints and storage requests (app.compose.reference_unknown), and every storage request must be mounted exactly by the services it lists, read-only for read access (app.compose.storage_mismatch).
- A key that needs an elevated right is refused in this version (Pithyx 1.0.0) with
app.elevated.unsupported.
| Key |
Type |
Required |
Limits |
Elevated right |
Description |
services |
services |
yes |
– |
– |
The containers of the backend, by service name. |
volumes |
volumeDeclarations |
no |
– |
– |
Named volumes, declared without options. Each one is a folder in the app’s dataset, or a storage request of the manifest with the same id. |
| Key |
Type |
Required |
Limits |
Elevated right |
Description |
image |
image |
yes |
– |
– |
The image, pinned by digest. The box takes the image for its architecture from backend/images.lock.json. |
command |
command |
no |
– |
– |
Replaces the command of the image. |
entrypoint |
command |
no |
– |
– |
Replaces the entrypoint of the image. |
environment |
environment |
no |
– |
– |
Environment variables of the container. |
working_dir |
string |
no |
Pattern containerPath |
– |
The working directory in the container. |
volumes |
mounts |
no |
– |
– |
Named volumes to mount, in the short form VOLUME:TARGET[:ro|rw] or the long form. Host paths are refused. |
tmpfs |
tmpfs |
no |
– |
– |
tmpfs mounts, PATH[:size=<size>,mode=<octal>]. |
depends_on |
dependsOn |
no |
– |
– |
Services to start before this one. |
healthcheck |
object |
no |
– |
– |
The health check of the container; it replaces the one of the image. |
healthcheck.test |
healthTest |
no |
– |
– |
The check: NONE, a command for the shell, [CMD, <program>, <argument>...] or [CMD-SHELL, <command>]. |
healthcheck.interval |
duration |
no |
1s to 1h |
– |
The time between two checks. |
healthcheck.timeout |
duration |
no |
1s to 1h |
– |
How long one check may take. |
healthcheck.start_period |
duration |
no |
0s to 1h |
– |
The time after the start in which failed checks do not count. |
healthcheck.start_interval |
duration |
no |
1s to 1h |
– |
The time between two checks during start_period. |
healthcheck.retries |
int |
no |
1 to 100 |
– |
Failed checks in a row after which the container counts as unhealthy. |
healthcheck.disable |
bool |
no |
– |
– |
Switches the health check of the image off. |
expose |
ports |
no |
– |
– |
Ports the service listens on for the other services of the app. Nothing is published on the host. |
read_only |
bool |
no |
– |
– |
Mounts the root file system of the container read-only. |
init |
bool |
no |
– |
– |
Runs an init process as PID 1 that forwards signals and reaps child processes. |
stop_signal |
string |
no |
Pattern signal |
– |
The signal that stops the container, for example SIGTERM or SIGINT. |
stop_grace_period |
duration |
no |
0s to 5m |
– |
How long the box waits after the stop signal before it kills the container. |
shm_size |
byteSize |
no |
at most 268435456 (256 MiB) |
– |
The size of /dev/shm. |
mem_limit |
byteSize |
no |
Pattern memorySize; 6291456 (6 MiB) to 274877906944 (256 GiB) |
– |
The memory limit, with a unit. |
cpus |
cpus |
no |
at most 256 |
– |
The CPU limit in cores, for example 0.5 or 2. |
labels |
labels |
no |
– |
– |
Labels of the container. Keys under com.pithyx and com.docker are reserved. |
network_mode |
enum |
no |
host |
host.network (using the key needs it) |
host: the container uses the host’s network instead of the app’s internal network. |
pid |
enum |
no |
host |
host.pid (using the key needs it) |
host: the container sees the processes of the host. |
devices |
devices |
no |
– |
device:<path> (using the key needs it) |
Device nodes, HOST[:CONTAINER[:PERMISSIONS]]; each device needs its own right device:<host path>. |
cap_drop |
capabilities |
no |
– |
– |
Linux capabilities to drop, without the CAP_ prefix. |
cap_add |
capabilities |
no |
– |
capability:<NAME> (using the key needs it) |
Linux capabilities to add, without the CAP_ prefix; each one needs its own right capability:<NAME>. |
privileged |
bool |
no |
– |
host.full (using the key needs it) |
A privileged container. The key needs host.full, which this version of Pithyx refuses. |
An entry of volumes in a service is either the short form VOLUME:TARGET[:ro|rw] or a map with these keys. The source is always a named volume declared under the top-level volumes; host paths and anonymous volumes are refused.
| Key |
Type |
Required |
Limits |
Elevated right |
Description |
type |
enum |
yes |
volume, tmpfs, bind |
– |
volume or tmpfs. bind is refused: host paths are never mounted. |
source |
string |
no |
– |
– |
The named volume, declared under the top-level volumes. Not allowed for tmpfs. |
target |
string |
yes |
Pattern containerPath |
– |
The path in the container. |
read_only |
bool |
no |
– |
– |
Mounts the volume read-only. Required for a storage request with read access. |
tmpfs |
object |
no |
– |
– |
Options of a tmpfs mount. |
tmpfs.size |
byteSize |
no |
at most 268435456 (256 MiB) |
– |
The size of the tmpfs. |
depends_on is a list of service names or a map from service name to these keys. Cycles are refused.
| Key |
Type |
Required |
Limits |
Elevated right |
Description |
condition |
enum |
no |
service_started, service_healthy, service_completed_successfully |
– |
When the other service counts as ready: started (the default in Compose), healthy, or finished with exit code 0. |
restart |
bool |
no |
– |
– |
Restarts this service when Compose restarts the other one. |
required |
bool |
no |
– |
– |
As in Compose; the other service must still be in this file. |
Mount targets that are refused (also / itself and everything below these), the Docker sockets that only a mount with the right shown may use, and the label prefixes an app cannot set:
/proc, /sys, /dev
/var/run/docker.sock, /run/docker.sock: docker.socket
com.pithyx, com.docker
backend/compose.dev.yaml is the source form that pithyx build reads; the box never reads it. Every rule above applies, plus build, and each service has exactly one of image and build. pithyx build writes compose.yaml with each build replaced by the built image pinned by digest. A catalog builds from this file, and every FROM of its Dockerfiles must be pinned by digest.
| Key |
Type |
Required |
Limits |
Elevated right |
Description |
build |
build |
no |
– |
– |
Where pithyx build builds the image of the service: a context folder relative to the file, or a map. compose.yaml gets the built image pinned by digest instead. |
build as a map:
| Key |
Type |
Required |
Limits |
Elevated right |
Description |
context |
string |
yes |
Pattern buildPath |
– |
The build context, a folder relative to the Compose file. |
dockerfile |
string |
no |
Pattern dockerfilePath |
– |
The Dockerfile relative to the context; default Dockerfile. |
target |
string |
no |
Pattern buildTarget |
– |
The stage of a multi-stage Dockerfile to build. |
args |
buildArgs |
no |
– |
– |
Build arguments, as a map or as a list of NAME=value. |
| Type |
Description |
services |
A map from service name (pattern serviceName) to a service, at least one and at most services entries. |
volumeDeclarations |
A map from volume name (pattern volumeName) to an empty value (data: or data: {}), at most volumes entries. Options are refused. |
image |
A string matching image. Without @sha256: it is refused as unpinned; longer than imageChars is refused. |
command |
A string, or a list of strings. |
environment |
A map from name to a string, a whole number or a boolean, or a list of NAME=value. Names match envName and appear once; every variable has a value. |
labels |
A map from key to a string, a whole number, a boolean or nothing, or a list of key=value. Keys match labelKey, appear once and do not start with a reserved prefix. |
string |
A string. A plain scalar that YAML reads as null, a boolean or a number is refused: quote it. With a pattern, the string must match it. |
bool |
true or false. |
int |
A whole number between the rule’s minimum and maximum. |
enum |
One of the listed strings. |
object |
A map with only the listed keys. |
duration |
A Compose duration such as 30s, 1m30s or 500ms (units h, m, s, ms, us), between the rule’s minimum and maximum. |
byteSize |
A number of bytes, or a number with the unit b, k, m or g (also kb, mb, gb; powers of 1024), above 0 and at most the rule’s maximum. |
cpus |
A number of cores above 0 with at most three decimals, as a number or a string, at most the limit cpus. |
mounts |
A list of mounts, each in the short form VOLUME:TARGET[:ro|rw] or the long form. Two mounts of a service cannot share a target. |
tmpfs |
A path, or a list of PATH[:OPTIONS]. The only options are size (a byte size up to tmpfsSizeBytes) and mode (octal). |
dependsOn |
A list of service names, or a map from service name to the long form. |
healthTest |
A string for the shell, or a list: [NONE], [CMD, <program>, <argument>...] or [CMD-SHELL, <command>]. |
ports |
A list of ports or port ranges a-b, optionally with /tcp or /udp, from 1 to 65535. |
devices |
A list of HOST[:CONTAINER[:PERMISSIONS]] with paths matching devicePath and permissions r, w or rw. Each host device appears once. |
capabilities |
A list of capability names matching capability, each once. |
build |
A context folder as a string, or a map with the keys of build. |
buildArgs |
Like environment. |
| Name |
Value |
Description |
fileBytes |
65536 (64 KiB) |
The largest Compose file, in bytes. |
depth |
10 |
The deepest nesting of maps and lists. |
entries |
256 |
The most entries of one list or map inside a service, such as environment or volumes. |
services |
16 |
The most services. |
volumes |
32 |
The most declared volumes. |
shmSizeBytes |
268435456 (256 MiB) |
The largest shm_size. |
tmpfsSizeBytes |
268435456 (256 MiB) |
The largest tmpfs, in a tmpfs entry or a long-form mount. |
imageChars |
512 |
The longest image reference. |
memoryMinBytes |
6291456 (6 MiB) |
The smallest mem_limit. |
memoryMaxBytes |
274877906944 (256 GiB) |
The largest mem_limit. |
cpus |
256 |
The largest cpus. |
Each pattern matches the whole value.
| Name |
Pattern |
Description |
serviceName |
^(?:[a-z0-9][a-z0-9_.-]{0,62})$ |
Service names. |
volumeName |
^(?:[a-z0-9][a-z0-9_-]{0,62})$ |
Volume names. |
image |
a pattern of 194 characters, see the schema |
Image references with an optional registry, an optional tag and the digest. |
imageDigest |
^(?:.*@sha256:[a-f0-9]{64})$ |
Whether a reference is pinned by digest at all. |
envName |
^(?:[A-Za-z_][A-Za-z0-9_]{0,127})$ |
Names of environment variables and build arguments. |
labelKey |
^(?:[A-Za-z0-9](?:[A-Za-z0-9._-]{0,126}[A-Za-z0-9])?)$ |
Label keys. |
containerPath |
^(?:/(?:[^/\u0000-\u001f\u007f:\\]+(?:/[^/\u0000-\u001f\u007f:\\]+)*)?)$ |
Absolute paths in a container, without :, \ or control characters; . and .. segments are refused as well. |
devicePath |
^(?:/dev(?:/[A-Za-z0-9_][A-Za-z0-9._-]*)+)$ |
Device paths below /dev. |
devicePermissions |
^(?:r|w|rw)$ |
Permissions of a device. |
capability |
^(?:(?!CAP_)[A-Z][A-Z0-9_]*)$ |
Linux capability names without the CAP_ prefix. |
signal |
^(?:SIG[A-Z][A-Z0-9]*(?:\+[0-9]{1,2})?)$ |
Signal names, optionally with +n for a real-time signal. |
duration |
a pattern of 97 characters, see the schema |
Durations. |
byteSize |
^(?:[0-9]{1,12}(?:[bBkKmMgG]|[kKmMgG][bB])?)$ |
Sizes with an optional unit. |
port |
^(?:[0-9]{1,5}(?:-[0-9]{1,5})?(?:/(?:tcp|udp))?)$ |
Ports and port ranges with an optional protocol. |
memorySize |
^(?:[0-9]{1,12}(?:[bBkKmMgG]|[kKmMgG][bB]))$ |
Sizes that must have a unit, for mem_limit. |
cpus |
^(?:(?:0|[1-9][0-9]{0,2})(?:\.[0-9]{1,3})?)$ |
CPU counts. |
tmpfsMode |
^(?:[0-7]{3,4})$ |
The octal mode of a tmpfs. |
buildPath |
a pattern of 94 characters, see the schema |
Build contexts relative to the Compose file, at most 16 segments. |
buildTarget |
^(?:[A-Za-z0-9][A-Za-z0-9_.-]{0,62})$ |
Names of Dockerfile stages. |
dockerfilePath |
a pattern of 170 characters, see the schema |
Dockerfile names: Dockerfile or Containerfile with an optional suffix, or <name>.Dockerfile, in at most 15 folders. |
Keys that are refused on purpose, with the reason from the rules file. Any other key that is not allowed above is refused as well.
| Key |
Why it is refused |
name |
the box sets the project name pithyx-<id> |
version |
obsolete in Compose v2 |
networks |
the box creates the app’s network |
secrets |
reads files or the box’s environment |
configs |
reads files or the box’s environment |
include |
reads other files |
x-* |
useless without anchors, which are refused |
| Key |
Why it is refused |
ports |
backends are reached only through the proxy |
build |
the box never builds images |
networks |
the box attaches the app’s network |
links |
service names resolve on the app’s network |
external_links |
reaches other projects |
volumes_from |
can reach another app’s volumes |
env_file |
reads files |
secrets |
reads files or the box’s environment |
configs |
reads files or the box’s environment |
label_file |
reads files |
extends |
reads other files |
profiles |
every service runs |
container_name |
the box names containers per project |
user |
the box sets the app uid, or root with backend.root |
group_add |
the box adds the share groups of mapped storage |
restart |
the box runs the lifecycle |
logging |
the box sets journald |
deploy |
use mem_limit and cpus |
scale |
one container per service |
mem_reservation |
use mem_limit |
memswap_limit |
use mem_limit |
mem_swappiness |
box policy |
cpu_shares |
use cpus |
cpu_quota |
use cpus |
cpu_period |
use cpus |
cpuset |
box policy |
pids_limit |
the box sets 1024 against fork bombs |
blkio_config |
box policy |
oom_kill_disable |
can stall the host |
oom_score_adj |
the box sets it so app containers go before its own services |
security_opt |
could switch off seccomp, AppArmor or no-new-privileges |
device_cgroup_rules |
could open every device |
gpus |
needs a runtime the box does not ship |
runtime |
needs a runtime the box does not ship |
isolation |
not on Linux |
userns_mode |
host would switch off the user namespace |
ipc |
host or another container’s IPC namespace |
uts |
host’s UTS namespace |
cgroup |
host’s cgroup namespace |
cgroup_parent |
would leave the app’s limits |
sysctls |
kernel settings |
ulimits |
box policy |
storage_opt |
box policy |
extra_hosts |
host-gateway reaches the box |
dns |
the app’s network resolves service names |
dns_search |
see dns |
dns_opt |
see dns |
hostname |
not needed on the app’s network |
domainname |
not needed on the app’s network |
mac_address |
network identity |
platform |
images come per architecture from images.lock.json |
pull_policy |
the box loads or pulls by digest itself |
tty |
no terminal |
stdin_open |
no terminal |
attach |
no terminal |
annotations |
not needed |
develop |
no development mode on the box |
post_start |
not in Compose 2.26 |
pre_stop |
not in Compose 2.26 |
x-* |
useless without anchors, which are refused |
A volume declaration has no options: volumes: { data: } or data: {} is all there is.
| Key |
Why it is refused |
driver |
volumes are folders in the app’s dataset |
driver_opts |
could mount any device or host path |
external |
reaches volumes outside the app |
name |
reaches volumes outside the app |
labels |
not needed |
| Key |
Why it is refused |
bind |
host paths are never mounted |
volume |
subpath and nocopy are not needed |
consistency |
not on Linux |