Start here
Stack recipes
Starter chalupa.yml files for web and database stacks, queues and search, and replica sets.
Reviewed 2026-10-02
Each recipe is a chalupa.yml you can copy next to your own
docker-compose.yml. They are generalised from the files Chalupa ships in its
examples/ directory and from real runs, with the service names, hosts and
addresses replaced by placeholders. Change the service names to match your
Compose file, set ssh.keys to the key chalupa setup registered, and replace
the example address in ssh.allowedCidrs with your own.
Three things hold for every recipe:
- Run
chalupa previewfirst. It is free, offline, and shows the exact Compose that would run and the tunnelled ports.chalupa inspectshows the server size and region. - Sizes are starting points, not promises. Resize with
chalupa resizeor editcompute.size. Your provider's price page is the authority for what a size costs per hour; Chalupa does not quote prices it has not measured. - Chalupa copies the services you list. It refuses
build,env_file,networksandprofilesin the Compose file, so use prebuilt images.
Web app with Postgres and Redis
The shape most test environments start with: an app, a relational database and a cache.
name: web-postgres-redis
provider: digitalocean
compose: ./docker-compose.yml
services: [web, postgres, redis]
overrides:
postgres:
mem_limit: 1g
redis:
# The environment is thrown away at teardown, so skip the disk snapshots.
command: ["redis-server", "--save", "", "--appendonly", "no"]
compute:
region: sfo3
size: s-2vcpu-4gb
health:
url: http://127.0.0.1:8080/
status: 200
timeoutSeconds: 300
ssh:
keys: [chalupa-my-laptop]
allowedCidrs: [203.0.113.10/32]
After chalupa up and chalupa tunnel, point your tests at localhost on the
ports your Compose file publishes. Add persist when you want the database to
survive teardown; see data and seeds.
Postgres with a one-shot migration
An application that must wait for its schema. Label the migration service in Compose so Chalupa knows it runs once and exits:
services:
postgres:
image: postgres:16-alpine
labels:
run.chalupa.role: database
run.chalupa.database.kind: postgresql
run.chalupa.database.primary: "true"
healthcheck:
test: ["CMD-SHELL", "pg_isready -U app -d app"]
interval: 5s
retries: 12
migrate:
image: postgres:16-alpine
labels:
run.chalupa.role: initializer
run.chalupa.initializes: postgres
depends_on:
postgres:
condition: service_healthy
api:
image: example/api:latest
ports:
- "127.0.0.1:8080:8080"
depends_on:
migrate:
condition: service_completed_successfully
name: migrate-then-serve
provider: digitalocean
compose: ./docker-compose.yml
services: [postgres, migrate, api]
compute:
region: sfo3
size: s-1vcpu-1gb
health:
url: http://127.0.0.1:8080/
status: 200
timeoutSeconds: 300
ssh:
keys: [chalupa-my-laptop]
allowedCidrs: [203.0.113.10/32]
Chalupa requires api to wait on migrate with service_completed_successfully and refuses the preview without it.
The examples/ci-db-migrate directory in the CLI package is the full version,
with a declared test suite.
MongoDB with a replica set
Transactions and change streams need a replica set. Chalupa can start MongoDB as a single-node set for you:
name: mongo-replica-set
provider: digitalocean
compose: ./docker-compose.yml
services: [mongo, api]
overrides:
mongo:
command: ["--wiredTigerCacheSizeGB", "2"]
mem_limit: 4g
ulimits:
nofile: { soft: 65536, hard: 65536 }
boot:
mongoReplicaSet: true
compute:
region: sfo3
size: s-4vcpu-8gb-amd
ssh:
keys: [chalupa-my-laptop]
allowedCidrs: [203.0.113.10/32]
persist:
sizeGb: 30
boot.mongoReplicaSet: true adds --replSet rs0 and initiates the set once the
stack is up, and leaves an already initiated data volume alone. Size the
WiredTiger cache and mem_limit together so MongoDB keeps room outside its
cache. The persist volume lets you seed once and reuse the data.
RabbitMQ and Elasticsearch
Two memory-hungry services. Set their heap and container limits explicitly so one does not starve the other:
name: queue-and-search
provider: digitalocean
compose: ./docker-compose.yml
services: [rabbitmq, elasticsearch, worker]
overrides:
rabbitmq:
# Chalupa cannot copy a host file that Compose bind-mounts, so drop the
# mount and carry its one setting as an environment variable.
omitBindMountTargets:
- /etc/rabbitmq/rabbitmq.conf
env:
RABBITMQ_SERVER_ADDITIONAL_ERL_ARGS: "-rabbit consumer_timeout 7200000"
elasticsearch:
env:
# Fixed heap, at most half the container limit, so Elasticsearch keeps
# room for native allocations and the filesystem cache.
ES_JAVA_OPTS: "-Xms2048m -Xmx2048m"
mem_limit: 4g
compute:
region: sfo3
size: s-4vcpu-16gb-amd
ssh:
keys: [chalupa-my-laptop]
allowedCidrs: [203.0.113.10/32]
Elasticsearch is the slowest of the pair to recover. If your tests start before
it has restored its shards, point compute.health at its cluster health
endpoint and give it a generous timeoutSeconds.
Temporal with Postgres
A workflow engine that must not start before its database is healthy:
name: temporal-postgres
provider: digitalocean
compose: ./docker-compose.yml
services: [postgres, temporal, temporal-ui]
overrides:
postgres:
healthcheck:
test: ["CMD-SHELL", "pg_isready -U temporal"]
interval: 5s
retries: 30
temporal:
waitFor: { postgres: healthy }
restart: on-failure:3
compute:
region: sfo3
size: s-4vcpu-8gb-amd
readiness:
- tcp: { service: postgres }
- http: { service: temporal-ui, path: / }
ssh:
keys: [chalupa-my-laptop]
allowedCidrs: [203.0.113.10/32]
waitFor becomes Compose's own depends_on condition, so Docker holds Temporal
until Postgres passes its healthcheck. compute.readiness declares probes that
all have to pass before your tests start: chalupa up --wait and
chalupa wait run them, and chalupa test waits for them before its specs;
see make servers ready faster.
Seed once, test many times
A large dataset you do not want to import on every run. The data volume lives in its own stack and survives teardown:
name: seeded-database
provider: digitalocean
compose: ./docker-compose.yml
services: [postgres, api]
persist:
sizeGb: 20
seed: ./scripts/import-demo-data.sh
compute:
region: sfo3
size: s-2vcpu-4gb
ssh:
keys: [chalupa-my-laptop]
allowedCidrs: [203.0.113.10/32]
chalupa data-up # once: creates the volume, asks for a typed phrase
chalupa up
chalupa tunnel # in another terminal
chalupa seed # runs your seed command on your laptop, through the tunnel
The next run starts from the seeded volume: chalupa up, test, chalupa down.
The volume bills monthly until you delete it on purpose. Read
data and seeds before you change its size or delete it.
Next
- Quickstart for your first run.
- The chalupa.yml reference for every field.
- Make servers ready faster when boot time is the problem.