Web Development

Stop Installing Postgres Locally—Docker Changed How I Setup Development

A

Admin User

Author

Jul 22, 2026
4 min read
16 views
Stop Installing Postgres Locally—Docker Changed How I Setup Development

I spent three years installing and reinstalling PostgreSQL directly on my machine. Each project had its own version pinned somewhere in a README I'd eventually lose. One day I'd upgrade locally for Project A, break Project B's migration scripts, then spend two hours figuring out which Postgres version I actually needed. That was before I fully embraced containers for my local setup.

The turning point was simple: I watched a junior developer on my team spend an entire morning debugging a connection error that didn't exist on my machine. Different Postgres versions, different extensions installed, different seed data. Docker would have solved that in thirty seconds. Since then, every project I start gets a docker-compose.yml before it gets a line of application code.

The Problem Docker Actually Solves

Here's what I realized: Postgres on bare metal works fine until it doesn't. You're managing versions, extensions, port conflicts, and cleanup manually. One project needs Postgres 14 with PostGIS, another needs 16 with just the defaults. You either maintain multiple installs (messy) or constantly upgrade/downgrade (fragile). Docker containers are disposable by design—spin one up per project, tear it down without touching anything else.

The beauty is reproducibility. When I commit a docker-compose.yml to version control, anyone cloning my repo gets the exact same database environment. No "works on my machine" database problems. No environment variance between devs.

Quick Start vs. Real Projects

The article distinguishes between throwaway containers and persistent development setups, and I think that's the right mental model.

For one-offs or testing, a quick docker run command works. I use that when I'm experimenting with a new feature or testing against different Postgres versions. Spin it up, run some queries, delete the container. No cleanup needed.

For actual projects—anything you'll return to—docker-compose is non-negotiable. It's version-controlled, it survives team changes, and it scales to multiple services without friction. Once you've got docker-compose.yml working, adding Redis or pgAdmin is just a few lines.

The Volume Question (Where Your Data Actually Lives)

This is where I see most developers trip up, and honestly, it's worth understanding deeply. Without a volume mount, your database lives inside the container's ephemeral layer. Delete the container, lose everything. That's intentional for throwaway testing, but it's a disaster for development work.

Named volumes (pgdata:/var/lib/postgresql/data) are Docker's default, and they're perfect for local dev—your data persists across container restarts and even docker compose down, but you don't manage messy host paths.

services:
  db:
    image: postgres:16
    environment:
      POSTGRES_USER: devuser
      POSTGRES_PASSWORD: secret
      POSTGRES_DB: myapp
    volumes:
      - pgdata:/var/lib/postgresql/data

volumes:
  pgdata:

If you need exact control over where data lives (I occasionally do for debugging), bind-mount a host directory instead: - ./pgdata:/var/lib/postgresql/data. Just remember that local paths are less portable across machines.

Health Checks Save You From Race Conditions

Here's a gotcha I learned the hard way: if your application spins up in the same compose file as Postgres, it'll try connecting before Postgres is actually ready. I watched this cause intermittent test failures for weeks.

The fix is a health check on the database service plus depends_on with a condition:

services:
  db:
    image: postgres:16
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U devuser -d myapp"]
      interval: 5s
      timeout: 5s
      retries: 5
  
  app:
    depends_on:
      db:
        condition: service_healthy

Now your app won't start until Postgres actually responds to connections. This single pattern has prevented more production issues than I'd like to admit.

What I'd Do Differently

I'm mostly aligned with the article's approach, but I'd add one thing: environment variables. Hardcoding database credentials in docker-compose.yml that sits in version control is a bad habit, even for local dev. I use a .env file:

environment:
  POSTGRES_USER: ${DB_USER}
  POSTGRES_PASSWORD: ${DB_PASSWORD}
  POSTGRES_DB: ${DB_NAME}

And .env stays in .gitignore. Seeding data automatically on first run is smart too, though I've found complex migrations often need to live in the app itself rather than in init scripts.

The Real Question

Docker makes local database development frictionless, but I think the bigger win is consistency. The same setup works for new team members, CI/CD, and your own muscle memory across projects. That matters more than the individual commands.

How are you currently managing Postgres for local development? If you're still installing it directly, what's holding you back from trying Docker?

Source: This post was inspired by "Running PostgreSQL with Docker" by Dev.to. Read the original article

Share this article

Written by Adil Sher

Full stack developer building high-traffic platforms, AI services, and custom web applications. Explore my portfolio, learn about my background, or get in touch.

Related Articles

I Pushed Code for Years Without Understanding What Happened Next
Web Development Aug 3

I Pushed Code for Years Without Understanding What Happened Next

I remember the exact moment I realized I had no idea how my CI pipeline actually worked. I was debugging a flaky test in our staging environment, and a senior developer asked me: "Where is this test running?" I said "GitHub Actions." He asked: "On what machine?" Silence. I honest...