Running & scaling
#Processes, not pets
ply run myapp.imgThe app runs in the foreground: stdout/stderr are your terminal, SIGTERM
and Ctrl-C work, the exit code propagates. Logs are stdout — pipe them
wherever you like. A tiny built-in init runs as PID 1 inside the container
so your app (PID 2) keeps normal signal semantics and zombies get reaped.
ply run --scale 3 myapp.img--scale N starts N identical instances — same image, same env, same
declared port. No per-instance port injection: each instance has its own
network namespace and IP, so they can all genuinely bind port 3000.
#Networking
Each instance gets a veth pair and an IP from the 10.77.0.0/16 bridge.
ply maintains <app>.ply names in the host's /etc/hosts, mapping to
instance IPs:
curl http://myapp.ply:3000 # round-robins across instances via DNSPorts in the manifest are labels of what the app binds internally — not host port claims. Two apps both declaring port 3000 never conflict.
For pretty URLs, TLS, and load balancing, ply deliberately does not proxy — it emits config for tools that already do it well:
ply proxy --backend caddy # Caddyfile for every running app
ply lb myapp --format nginx # one app's backend poolInstances of the same app round-robin automatically in the emitted config.
#Environment
Composition order (last wins): package contributions → manifest [env] →
-e KEY=VALUE → --env-file.
ply run -e NODE_ENV=production --env-file /etc/myapp/secrets.env myapp.imgNever bake secrets into an image — it's a file people scp around. Use
--env-file with a root-only file at run time.
#Dev mode
ply run --link ./src:/opt/myapp myapp.imgBind-mounts live code over the image's app layer — edit on the host, run in the container, no rebuild loop.
#Observing
ply ps # instances: pid, ip, uptime, health, restarts
ply ps --json # machine-readable, for scripts and CI
ply stats # live CPU%, memory, pids, net rx/tx, throttling
ply stats myapp.2 # one instance
ply exec myapp sh # shell into instance 1 of myapp
ply exec myapp.2 sh # a specific instanceply stats reads cgroup v2 files and veth counters straight from the
kernel — there is no metrics agent.
#Supervision
Supervision is systemd's job:
ply systemd myapp.img > /etc/systemd/system/myapp.service
systemctl enable --now myappFor in-process restarts (crash loops with backoff) see
[restart] — the run parent can respawn failed instances
itself, without systemd.
#Cleaning up
ply rm myapp # stop + remove instances (volumes kept)
ply rm myapp --volumes # …and destroy its data (always explicit)
ply gc # delete store entries no app references