Databases in Podman, your code as processes. Best of both worlds, orchestrated cleanly.
I need Postgres and Redis for local dev. Plus my Rust API with hot reload and a Vite frontend
Docker Compose everything? Even with mounted volumes, hot reload is slow. File watchers miss changes. Vite’s HMR breaks half the time. File permissions mess everything up on Linux
Running everything as native processes? Now I’m managing Postgres versions and Redis configs manually
Mix both. Databases in containers. Code as processes. One tool to orchestrate
What process-compose does
It’s an orchestrator for processes and containers. Not just one or the other. Both
Define what runs, dependencies, health checks. It starts everything in order, monitors, restarts failures, unified logs
version: "0.5"
processes:
postgres:
command: podman run --rm -p 5432:5432 -e POSTGRES_PASSWORD=dev postgres:15
readiness_probe:
exec:
command: pg_isready -h localhost
initial_delay_seconds: 3
redis:
command: podman run --rm -p 6379:6379 redis:7
api:
command: cargo watch -x run
depends_on:
postgres:
condition: process_healthy
redis:
condition: process_healthy
frontend:
command: npm run dev
depends_on:
api:
condition: process_healthy Databases run in Podman. Official images, zero config. Your code runs native with hot reload
One process-compose command, everything starts in the right order
Why containers for databases
Postgres, Redis, Elasticsearch. They have official images. Versioned, tested, ready
Running Postgres 15? podman run postgres:15. Done. No package manager fights. No version conflicts. No leftover configs
Need Postgres 14 for another project? Different port, different container. They don’t clash
Containers isolate databases. Your code changes don’t touch them. Their state persists in volumes. Clean separation
Why processes for your code
Rust with cargo watch. TypeScript with Vite. Go with air. They have hot reload built in
Wrap them in containers? Hot reload breaks. File watchers don’t see changes. Rebuilds are slow
Run them as processes. File changes trigger instant recompilation. HMR works. Debuggers attach easily
Your code is what changes constantly. Keep it fast
Real config for a web stack
version: "0.5"
environment:
- DATABASE_URL=postgresql://postgres:dev@localhost:5432/myapp
- REDIS_URL=redis://localhost:6379
processes:
db:
command: podman run --rm -p 5432:5432 -v postgres-data:/var/lib/postgresql/data -e POSTGRES_PASSWORD=dev postgres:15
availability:
restart: on_failure
readiness_probe:
exec:
command: pg_isready -h localhost
initial_delay_seconds: 2
period_seconds: 1
redis:
command: podman run --rm -p 6379:6379 redis:7
availability:
restart: on_failure
readiness_probe:
exec:
command: redis-cli -h localhost ping
initial_delay_seconds: 1
migrate:
command: sqlx migrate run
depends_on:
db:
condition: process_healthy
availability:
restart: no
api:
command: cargo watch -x 'run --bin api'
depends_on:
db:
condition: process_healthy
migrate:
condition: process_completed
redis:
condition: process_healthy
readiness_probe:
http_get:
host: localhost
port: 8080
path: /health
period_seconds: 2
frontend:
command: npm run dev
depends_on:
api:
condition: process_healthy
working_dir: ./frontend Postgres and Redis in containers. Database migration runs once. Rust API with cargo watch for hot reload. Vite frontend depends on API being healthy
Change your Rust code. cargo watch recompiles in 500ms. No image rebuild. No container restart
The TUI shows everything
Run process-compose and you get a terminal UI:
- All processes, containers and native
- Status: running, healthy, failed
- Live logs
- Dependencies visualized
- Restart with
r
Postgres container logs. Rust compilation output. Vite HMR messages. All in one place
Press 1 for process 1 logs. Press l for all logs. Press q to quit. Everything stops cleanly
Health checks that actually work
processes:
api:
command: cargo watch -x run
readiness_probe:
http_get:
host: localhost
port: 8080
path: /health
initial_delay_seconds: 5
period_seconds: 3
timeout_seconds: 2 Health checks for native processes. HTTP, exec, TCP. Like Kubernetes, but local
Frontend doesn’t start until API is healthy. Not just “started”, actually serving requests
Containers get health checks too:
processes:
db:
command: podman run --rm postgres:15
readiness_probe:
exec:
command: pg_isready -h localhost Dependencies wait for real health. No race conditions
Combine with Devbox
Devbox for your dev tools. process-compose to run them
{
"packages": [
"podman",
"postgresql",
"redis",
"rustup",
"nodejs",
"process-compose"
],
"shell": {
"init_hook": [
"rustup default stable"
]
}
} devbox shell gives you Podman, Postgres client tools, Node, Rust, and process-compose. Same versions everywhere
Add .envrc:
use devbox
export DATABASE_URL=postgresql://postgres:dev@localhost:5432/myapp
export REDIS_URL=redis://localhost:6379 direnv allow loads everything. process-compose starts your stack
Devbox handles tools. Podman handles databases. process-compose orchestrates. Clean split
When to use containers for code
Building for production. You need the exact runtime environment
Testing the actual Dockerfile. Making sure it works before deploy
Multi-stage builds where containerization is part of the build process
But daily dev with hot reload? Processes win
If you’re obsessed with containers
You really love Docker Compose and running a podman run command feels wrong? Use Docker Compose INSIDE process-compose
version: "0.5"
processes:
infra:
command: docker compose -f infra.yml up
working_dir: ./infra
shutdown:
command: docker compose -f infra.yml down
api:
command: cargo watch -x run
depends_on:
infra:
condition: process_healthy
frontend:
command: npm run dev
depends_on:
api:
condition: process_healthy Your infra.yml contains all your containerized services (Postgres, Redis, etc.), and process-compose launches it as a single process. Keep your Docker Compose for infrastructure, run your code as native processes
The best part? When you quit process-compose, it automatically runs docker compose down. Everything cleans up properly
Multiple environments, same config
Dev uses containers for databases:
processes:
db:
command: podman run --rm postgres:15 CI uses actual Postgres:
processes:
db:
command: postgres -D /ci/postgres Same process-compose.yaml, different environment. Override with process-compose -f base.yaml -f ci.yaml
Getting started
Install process-compose:
# Arch
yay -S process-compose
# With Devbox
devbox add process-compose
# Manual
wget https://github.com/F1bonacc1/process-compose/releases/latest/download/process-compose_linux_amd64.tar.gz
tar xf process-compose_linux_amd64.tar.gz
sudo mv process-compose /usr/local/bin/ Create process-compose.yaml:
version: "0.5"
processes:
db:
command: podman run --rm -p 5432:5432 -e POSTGRES_PASSWORD=dev postgres:15
app:
command: cargo watch -x run
depends_on:
db:
condition: process_started Run:
process-compose Postgres container starts. Rust app waits, then runs with hot reload. Logs stream to TUI
Change code. cargo watch recompiles. No container rebuilds
Real comparison
Docker Compose for everything:
- Databases: works well
- Code: slow rebuilds, broken hot reload, volume permission issues
- Logs:
docker compose logs -f service - Restart:
docker compose restart - Code change: rebuild image
process-compose mixing both:
- Databases: containers, official images
- Code: native processes, hot reload works
- Logs: unified TUI
- Restart: press
r - Code change: hot reload triggers automatically
I switched a Rust + TypeScript project to this setup. Code changes went from 8-second Docker rebuilds to 500ms hot reloads. Vite HMR started working. Debugging got easier
Databases still in containers. Isolated, reproducible. Code as processes. Fast, debuggable
Best of both worlds
Enjoyed this article?
Let me know! A share is always appreciated.