Back to articles
blog — devops — zsh$ cat devops.md# Nov 22, 2025Process-compose: DockerCompose without containers
4 min read

Databases in Podman, your code as processes. Best of both worlds, orchestrated cleanly.

DockerDevOpsDevelopmentTools

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.

About the author

Sofiane Djerbi

Sofiane Djerbi

Cloud & Kubernetes Architect, FinOps Expert. I help companies build scalable, secure, and cost-effective infrastructures.

Comments