Speed and cost
Make servers ready faster
Where boot time goes, what a prebaked image changes, how to check readiness, and how to reuse a server, a cache and a build across runs.
Reviewed 2026-10-04
A server is useful when your services answer, not when the provider says it
exists. This page shows where the time goes between chalupa up and a ready
stack, and what you can change today to make repeat runs faster.
Where the time goes
- The server boots. The provider creates the machine. This is the part you cannot shorten.
- The host is prepared. On a stock image, cloud-init installs Docker and the tools Chalupa needs, then writes your per-environment files.
- Images are pulled. Every service image is pulled from its registry on the new server, every time.
- Your own app images arrive. Compose
build:is not supported (see what Compose features are supported), so an app image you build yourself has to be built and shipped to the server.chalupa apps buildandchalupa apps upprint the script for that; they do not run it. - Data is seeded. Importing a large dataset can take longer than all of the above.
- Your services start. A database that recovers a large index, such as Elasticsearch restoring its shards, can take minutes on its own.
One measured stack
These are the only measured figures Chalupa has, from one anonymised stack on one provider in August 2026, on an image that already had Docker installed:
- The server and its Docker setup were ready in 4 to 5 minutes.
- Stacks that also built and shipped their own app images took 10 to 40 minutes to be ready.
A first-time user boots a stock image, which installs Docker first, so expect
more. Your stack, region and registry will change every number. chalupa receipt prints the time to ready of each session on your own machine, so
measure your stack instead of trusting these figures (see
session receipts).
Prebaked images
Status: Beta. A prebaked image already ships Docker, the tools, static scripts, system users and pre-enabled systemd units. cloud-init then skips package installation and writes only your per-environment payload.
compute:
image: "123456789" # the id of your golden snapshot
prebaked: true
prebaked: true requires image to be a golden snapshot. The image masks
background apt jobs, so boxes booted from it never run apt on their own, and
picking up operating system updates means baking a new one. It also records
what it contains in /etc/chalupa/golden.json: the Chalupa version, the bake
time, the provider and the Monitor version.
Build the image with chalupa snapshot build --config PATH --confirm "bake <name>":
it prints the estimated cost and time first, creates a temporary billable
server, and changes nothing without --confirm. The setup logs in with the
ssh.identityFile key (or CHALUPA_TASK_SSH_IDENTITY); with neither, the
ssh-agent must hold a key matching one of the account keys the bake selected,
which is checked before the server exists (an agent emptied by a reboot is
refused with a message, not discovered after the host is billing). Set compute.image: golden:latest to follow the newest golden this machine baked, and
chalupa snapshot status lists them with their age. The agent, forwarder and
Monitor are refreshed over SSH on every chalupa up, so the image does not
freeze them, and chalupa doctor --config warns when the golden in use is
stale. See compute.
Readiness probes
compute.readiness declares typed probes that must all pass before a stack
counts as up: tcp, http, mongo, amqp and a cmd escape hatch. They
replace hand-written wait scripts.
compute:
readiness:
- tcp: { service: postgres }
- http: { service: web, path: /healthz }
- cmd: { command: "node scripts/wait-es.mjs", timeoutSeconds: 30 }
Probes run on your machine against the tunnel, so the tunnel has to be open.
chalupa wait runs them through it and prints when each became ready;
chalupa up --wait opens a supervised tunnel and waits for them before it
returns, and the same probes gate the specs of chalupa test and the command
of chalupa exec. See compute.readiness.
compute.health is a separate, single check: chalupa up and chalupa test
both wait for it through a temporary tunnel and fail if it never answers, with
or without --wait. --wait adds the compute.readiness probes on top.
chalupa preview shows the declared probes offline.
Do not rebuild a server mid-run
Rebuilding a server throws away everything above. If a run needs more time:
chalupa session extend --hours 2lengthens the session while the server is still running. It is billable, so it asks for a typed confirmation.chalupa session renewre-leases an expired session whose server is still running, withinsession.maxLifetimeHours.
Seed once
A large import should not run on every launch. Declare persist and run
chalupa data-up once, then chalupa seed. The data volume lives in its own
stack and survives teardown, so the next chalupa up starts from the seeded
data. It bills monthly until you delete it on purpose. For a large dataset,
chalupa data load uploads it once and a restore recipe rebuilds the databases
on the server, with nothing crossing SSH per run. See
data and seeds.
Reuse a server, a cache and a build
Each of these is opt-in, declared in chalupa.yml, and shown when you confirm.
- Warm server. Declare
session.keepFor(for example45m).chalupa session releasethen keeps the server for that window instead of sinking it, and the nextchalupa upreuses it if the config is unchanged.chalupa up --reuse-only --config PATHreuses a warm server or refuses with a reason and never launches. The server bills until the window ends. See Warm boxes and reuse. - App swap. For your own app images,
chalupa apps swap SERVICE --from REFreplaces one service's code on the running server without rebuilding the image. Walkthrough below. - Cache volume. A
cache:block andchalupa cache-upcreate a separate, disposable<env>-cachevolume that holds Docker's images and build cache, so they survivechalupa downand repeat boots skip image pulls. Walkthrough below. - Run receipts.
chalupa receiptprints one line per session: how long it was alive, time to ready, flows, idle time and estimated cost. See session receipts.
Swap code without rebuilding an image
Status: Beta. A swap puts a code-only change into an app image that is
already running on your server, without rebuilding the image. It needs a server
in your own cloud account and a swap block on the image in
apps.images[].
Declare it. Name the paths to replace and, if the code needs compiling, the local command that produces them:
apps: images: - name: web dockerfile: ./Dockerfile swap: build: bun run build copy: - from: dist to: /app/dist build: mode: remoteRead the plan for free.
chalupa apps swap web --from HEAD --dry-runprints the plan and the derived Dockerfile and opens no SSH connection.Swap.
chalupa apps swap web --from HEADasks you to typeswap <environment>(or takes it with--confirmand--config). It exports the ref cleanly, so only committed files ship, runs yourswap.buildon your machine, builds an image from the one already running plus those paths, restarts only that service, and runs its readiness probes.--fromtakes a git ref orHEAD.See what is on the box.
chalupa apps lslists the swap tags, the pinned base images and which service runs which. It is read-only.Switch back without a rebuild. The first swap pins the original image as
base-<id12>, and each swap keeps the previous tag (--keep Nchanges how many).chalupa apps swap web --use-tag baseswitches to the original, or to any kept 12-character tag, so a control run needs no rebuild.
Exit codes: 0 swapped, 1 swapped but not ready (also when no declared readiness
probe could run), 2 phrase mismatch, 64 refusal, 69 box or tool failure.
--no-ready skips the probes and --json prints one JSON document.
A swap replaces only the paths you declared, and warns when it can see that
dependency files changed since the image was built. A change to dependencies or the
Dockerfile needs a real build with chalupa apps build. Uncommitted changes are
never shipped.
Create a cache
cache:
sizeGb: 50
pruneAfterDays: 14
chalupa cache-up --config ./chalupa.yml # type: cache <environment>
chalupa up --config ./chalupa.yml # attaches it when it exists
chalupa cache-nuke --config ./chalupa.yml # type: drop cache <environment>
cache-up creates the separately billed <env>-cache volume. The next
chalupa up attaches it as Docker's data directory, so images pulled on one run
are already there on the next. A config that declares cache: still launches
before cache-up: it just boots from the boot disk, and the launch says so. The
volume bills monthly until cache-nuke deletes it, which refuses while a server
is attached and never touches the protected data volume. With
cache.retireAfterIdleHours the cache is retired automatically after that many
idle hours and recreated, empty, by the next chalupa up (see
Warm-day storage).
pruneAfterDays
removes unused images built that many days ago or more; it counts the
image's build date, not its last use. See cache.
Status
| Piece | Status | What it does |
|---|---|---|
| Prebaked server image | Beta | compute.prebaked: true boots from an image that already has Docker and the tools. You build it with chalupa snapshot build. |
| Warm server | Beta | session.keepFor plus chalupa session release keeps a server between runs, and chalupa up reuses it. For servers in your own cloud account; a changed config needs --fresh. |
| App swap | Beta | chalupa apps swap replaces one service's code on a running server without rebuilding the image. Declared per image; your own cloud account only. |
| Cache volume | Beta | cache: and chalupa cache-up keep Docker's images and build cache across teardown in a disposable volume. DigitalOcean and Hetzner only. |
| Run receipts | Beta | chalupa receipt and chalupa status --timeline read a local ledger. Costs are estimates from the provider's price, not an invoice. |
Beta means each of these ships today and the matrix says what it does not cover. Prebaked images, a seeded data volume and a longer session remain the other ways to avoid a rebuild.