docs

Running & scaling

#Processes, not pets

ply run myapp.img

The 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 DNS

Ports 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 pool

Instances 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.img

Never 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.img

Bind-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 instance

ply 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 myapp

For 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