Skip to content

Data Stores

🧱 Where The Data Lives

Almost every interesting service becomes much easier to live with once the data layer is handled properly.

This is the unglamorous part of the lab, but it is also the part that makes the rest possible: databases, caches, and document stores that other stacks quietly depend on.

🐘 PostgreSQL

PostgreSQL Logo

Default SQL

PostgreSQL is the database I reach for when I want something solid and unsurprising. A lot of the services in this lab get better the moment they stop using an embedded database and move onto Postgres, so I keep this recipe around as the dependable baseline.

Config

This recipe uses a named volume for persistence, an external back network so other stacks can join it, a host port for direct access, and a health check with pg_isready so dependent services can tell the difference between "container started" and "database ready."

Compose

name: postgresql

volumes:
  postgresql:
    name: ${POSTGRES_VOLUME:-postgresql}

networks:
  back:
    name: ${NETWORK_BACK:-back}
    external: true

services:
  postgresql:
    image: postgres:${POSTGRES_VERSION:-alpine}
    container_name: postgresql
    networks:
      - back
    ports:
      - ${POSTGRES_PORT:-5432}:5432
    volumes:
      - postgresql:/var/lib/postgresql/data
    environment:
      POSTGRES_DB: ${POSTGRES_DB:?POSTGRES_DB; no database name}
      POSTGRES_USER: ${POSTGRES_USER:?POSTGRES_USER; no database user}
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?POSTGRES_PASSWORD; no database password}
    healthcheck:
      interval: 30s
      start_period: 20s
      timeout: 10s
      retries: 5
      test:
        - CMD-SHELL
        - pg_isready -d $${POSTGRES_DB} -U $${POSTGRES_USER}
    mem_reservation: 256mb
    restart: always

Example env

NETWORK_BACK=back
POSTGRES_VERSION=17-alpine
POSTGRES_PORT=5432
POSTGRES_DB=app
POSTGRES_USER=appuser
POSTGRES_PASSWORD=change-me
POSTGRES_VOLUME=postgresql

Vars

Variable Purpose Why it matters
POSTGRES_VERSION Image tag Pin this when you want calmer upgrades
POSTGRES_PORT Host port Useful if 5432 is already taken
POSTGRES_DB Initial database Often set per app or per shared instance
POSTGRES_USER Database user The application logs in with this
POSTGRES_PASSWORD Database password Required; should be secret-managed in stricter setups
POSTGRES_VOLUME Named volume Keeps data alive across recreation
NETWORK_BACK Backend network name Lets app stacks reach the same database

Redis

Redis Logo

Fast Glue

Redis is the little helper that keeps showing up once a stack gets even slightly serious. I mostly use it for cache, sessions, queues, and general "this app wants a bit of shared speed" duties.

Config

This file mirrors the Postgres pattern: named volume, external back network, exposed port, and a redis-cli ping health check. The custom command keeps persistence enabled while toning down the log noise.

Compose

name: redis

volumes:
  redis:
    name: ${REDIS_VOLUME:-redis}

networks:
  back:
    name: ${NETWORK_BACK:-back}
    external: true

services:
  redis:
    image: redis:${REDIS_VERSION:-alpine}
    container_name: redis
    networks:
      - back
    ports:
      - ${REDIS_PORT:-6379}:6379
    volumes:
      - redis:/data
    command: --save 60 1 --loglevel warning
    healthcheck:
      interval: 30s
      start_period: 20s
      timeout: 10s
      retries: 5
      test:
        - CMD-SHELL
        - redis-cli ping | grep PONG
    mem_reservation: 32mb
    restart: always

Example env

NETWORK_BACK=back
REDIS_VERSION=7-alpine
REDIS_PORT=6379
REDIS_VOLUME=redis

Vars

Variable Purpose Why it matters
REDIS_VERSION Image tag Pinning avoids surprise upgrades
REDIS_PORT Host port Useful for direct debugging
REDIS_VOLUME Named volume Keeps state across restarts
NETWORK_BACK Backend network Lets app stacks reach Redis

🛋️ CouchDB

CouchDB Logo

Document Store

CouchDB is the one I keep around when I want to think in JSON documents instead of tables. I like it for experiments where an HTTP-first database and replication-friendly ideas make more sense than forcing everything into SQL.

Config

This is a deliberately small recipe: one named volume, one host port, and admin credentials through environment variables. It is a clean example of a single stateful service without extra helper containers.

Compose

name: couchdb

volumes:
  couchdb-data:
    name: ${COUCHDB_VOLUME:-couchdb-data}

services:
  couchdb:
    image: couchdb:${COUCHDB_VERSION:-latest}
    container_name: couchdb
    ports:
      - "${COUCHDB_PORT:-5984}:5984"
    volumes:
      - couchdb-data:/opt/couchdb/data
    environment:
      COUCHDB_USER: ${COUCHDB_USER}
      COUCHDB_PASSWORD: ${COUCHDB_PASSWORD}
    restart: unless-stopped

Example env

COUCHDB_VERSION=3
COUCHDB_USER=admin
COUCHDB_PASSWORD=change-me
COUCHDB_PORT=5984
COUCHDB_VOLUME=couchdb-data

Vars

Variable Purpose Why it matters
COUCHDB_VERSION Image tag latest is convenient, but pinning is safer
COUCHDB_USER Admin user Needed for setup and admin access
COUCHDB_PASSWORD Admin password Should be strong; it protects the API
COUCHDB_PORT Host port Default is 5984
COUCHDB_VOLUME Named volume Keeps document data persistent

Comments