← all articles
devops

PostgreSQL & Redis Local Setup with Docker Compose

Run a real Postgres database and Redis cache locally in two containers, with data that survives restarts and credentials that stay out of Git.

3 min read·July 16, 2026

Installing Postgres and Redis natively works, but you end up with whatever version your package manager shipped, running as a background service you have to remember exists. Running them in containers instead means each project pins its own versions, and docker compose down cleanly stops everything with nothing left running in the background. This builds on the Docker setup guide — have Docker running before you start.

The compose file

# docker-compose.yml
services:
  postgres:
    image: postgres:16
    restart: unless-stopped
    environment:
      POSTGRES_USER: ${POSTGRES_USER}
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
      POSTGRES_DB: ${POSTGRES_DB}
    ports:
      - "5432:5432"
    volumes:
      - pg-data:/var/lib/postgresql/data
 
  redis:
    image: redis:7
    restart: unless-stopped
    ports:
      - "6379:6379"
    volumes:
      - redis-data:/data
    command: redis-server --appendonly yes
 
volumes:
  pg-data:
  redis-data:

Keep credentials out of the compose file

# .env
POSTGRES_USER=devuser
POSTGRES_PASSWORD=devpassword
POSTGRES_DB=myapp_dev

Docker Compose reads a .env file in the same directory automatically — that's what ${POSTGRES_USER} above resolves from. Add it to .gitignore immediately:

echo ".env" >> .gitignore
warning

Commit a .env.example with the same keys and placeholder values instead, so teammates know what to fill in without ever seeing real credentials in Git history.

Start it

docker compose up -d
docker compose ps

Both containers should show as running. Data written to either one persists across restarts because of the named volumes — docker compose down (without -v) stops the containers but keeps the volumes intact.

Connect to Postgres

From your host machine, if you have psql installed:

psql -h localhost -U devuser -d myapp_dev

Or skip installing a client entirely and use the one already inside the container:

docker compose exec postgres psql -U devuser -d myapp_dev

From application code, the connection string is just:

postgresql://devuser:devpassword@localhost:5432/myapp_dev

Connect to Redis

docker compose exec redis redis-cli
127.0.0.1:6379> set greeting "hello"
OK
127.0.0.1:6379> get greeting
"hello"

From application code: redis://localhost:6379.

A GUI, if you want one

Command-line clients are fine for quick checks; for browsing data regularly, TablePlus, DBeaver (Postgres), and RedisInsight (Redis) all connect to localhost on the ports above with zero extra config.

Common gotchas

warning

"port is already allocated" on 5432 or 6379. You likely have a native Postgres or Redis install already running on the default port. Either stop that service, or remap the container's port — "5433:5432" in the compose file, then connect on 5433 instead.

warning

Data disappeared after docker compose down. If you ran docker compose down -v, the -v flag deletes volumes along with containers — that's the one that wipes data. Plain docker compose down does not.

tip

Add a health check so dependent services (like an app container with depends_on) actually wait for Postgres to be ready, not just started:

healthcheck:
  test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER}"]
  interval: 5s
  timeout: 5s
  retries: 5

With both services defined in one file next to your app's code, "set up the database" for a new contributor becomes docker compose up -d — nothing to install, nothing to remember to start manually.