{"post":{"slug":"how-to-set-up-postgres-locally-2026","title":"How to set up Postgres locally in 2026: Docker, pgvector, and dev workflow","excerpt":"Running Postgres locally in 2026 means Docker Compose, pgvector for AI workloads, and a repeatable seed script. This guide gets you from zero to a productive dev database.","category":"code","date":"March 16, 2026","author":"ByteLedger Team","content":"<span class=\"dropcap\">A</span> local Postgres setup sounds trivial until you're debugging a schema mismatch between dev and production, or you realize pgvector isn't installed and your migration has already run. The right setup is Docker Compose with an explicit version pin, a seed script, and the extensions you'll use in production baked in from day one. Here's the full setup.\n\n## What changed in 2026\n\n- **Postgres 16/17** are the current stable branches; Postgres 16 is the safe production default, and 17 is gaining adoption.\n- **pgvector 0.8+** supports HNSW indexes natively, making local AI/embedding development fast without a separate vector database.\n- **`pgvector/pgvector:pg16`** is an official Docker image that includes pgvector out of the box — no manual extension install needed.\n- **Neon and Supabase** offer Postgres-compatible cloud dev branches, so some teams skip local Docker entirely. Both work well for solo or small-team development.\n\n## Docker Compose setup\n\n```yaml\n# docker-compose.yml\nservices:\n  postgres:\n    image: pgvector/pgvector:pg16   # or postgres:16 if you don't need pgvector\n    container_name: dev_postgres\n    restart: unless-stopped\n    environment:\n      POSTGRES_USER: devuser\n      POSTGRES_PASSWORD: devpass\n      POSTGRES_DB: myapp_dev\n    ports:\n      - \"5432:5432\"\n    volumes:\n      - postgres_data:/var/lib/postgresql/data\n      - ./db/init:/docker-entrypoint-initdb.d  # SQL files run on first start\n\nvolumes:\n  postgres_data:\n```\n\nStart it:\n\n```bash\ndocker compose up -d postgres\ndocker compose logs -f postgres   # watch for \"ready to accept connections\"\n```\n\nStop without losing data:\n\n```bash\ndocker compose stop postgres\n```\n\nFull reset (wipe data):\n\n```bash\ndocker compose down -v\n```\n\n## Connection string\n\n```bash\n# .env\nDATABASE_URL=postgresql://devuser:devpass@localhost:5432/myapp_dev\n```\n\nWith asyncpg (Python):\n```\nDATABASE_URL=postgresql+asyncpg://devuser:devpass@localhost:5432/myapp_dev\n```\n\nWith Node (pg / Drizzle / Prisma):\n```\nDATABASE_URL=postgresql://devuser:devpass@localhost:5432/myapp_dev\n```\n\n## Enable pgvector\n\nIf using the pgvector image, enable it in your first migration or init script:\n\n```sql\n-- db/init/00_extensions.sql\nCREATE EXTENSION IF NOT EXISTS vector;\nCREATE EXTENSION IF NOT EXISTS pg_trgm;  -- for trigram full-text search\nCREATE EXTENSION IF NOT EXISTS unaccent; -- helpful for search normalization\n```\n\nFiles in `docker-entrypoint-initdb.d/` run alphabetically on first container start.\n\n## Seed script\n\n```ts\n// scripts/seed.ts (Node/Drizzle example)\nimport { db } from '../src/db/client.js';\nimport { users } from '../src/db/schema.js';\n\nasync function seed() {\n  await db.insert(users).values([\n    { email: 'alice@example.com', name: 'Alice' },\n    { email: 'bob@example.com', name: 'Bob' },\n  ]).onConflictDoNothing();\n  console.log('Seeded users');\n  process.exit(0);\n}\n\nseed().catch((e) => { console.error(e); process.exit(1); });\n```\n\nAdd to `package.json`:\n```json\n\"scripts\": {\n  \"db:seed\": \"tsx scripts/seed.ts\",\n  \"db:reset\": \"docker compose down -v && docker compose up -d postgres && sleep 2 && npm run db:migrate && npm run db:seed\"\n}\n```\n\n## GUI tools comparison\n\n| Tool | Platform | Best for |\n| --- | --- | --- |\n| TablePlus | macOS/Windows | Fast, native, paid (~$90) |\n| DBeaver | All | Free, full-featured, heavier UI |\n| pgAdmin 4 | Web/all | Official, free, verbose |\n| DataGrip | All | JetBrains ecosystem, best SQL editor |\n| Postico 2 | macOS | Minimal, fast, affordable |\n\nFor quick queries, `psql` in the container works fine:\n\n```bash\ndocker exec -it dev_postgres psql -U devuser -d myapp_dev\n```\n\n## How to keep dev and prod in sync\n\n1. **Pin the same Postgres major version** in both Docker Compose and your managed DB (RDS, Cloud SQL, Neon).\n2. **Run migrations in CI** against a fresh Docker Compose DB to catch schema issues before they hit production.\n3. **Never run manual SQL** in dev and skip writing a migration — future you will be confused.\n4. **Use `pg_dump` occasionally** to snapshot your local schema and compare against production.\n\n## Common mistakes\n\n**Using `latest` as the image tag.** A `docker pull` six months later upgrades your Postgres silently. Pin to `pgvector/pgvector:pg16`.\n\n**Forgetting `volumes:`** for data persistence. Without a named volume, every `docker compose down` wipes your database.\n\n**Opening port 5432 on a VM or cloud instance.** For dev-only, keep 5432 on `127.0.0.1` only. In Docker, `\"127.0.0.1:5432:5432\"` binds only to localhost.\n\n**Not running `ANALYZE`** after bulk seed inserts. The query planner uses stale statistics until you run `ANALYZE` or `VACUUM ANALYZE`.\n\n**Sharing one dev DB across the team.** Each developer should have their own local container; shared dev DBs become a coordination problem.\n\n## What to skip\n\n- **Native Postgres installs** on macOS/Linux for new projects — Docker is portable and matches the prod OS.\n- **Homebrew Postgres** unless you specifically need it; version upgrades are manual and messy.\n- **Port-forwarding a staging DB as your local DB** — mutations in dev will corrupt staging data.\n\n## FAQ\n\n**How do I connect a Prisma app to the local DB?**\nSet `DATABASE_URL` in `.env`, run `npx prisma db push` for schema sync in dev, or `npx prisma migrate dev` for migration-tracked changes.\n\n**What Postgres version should I use?**\nMatch your production version exactly. If production is RDS Postgres 16, use `postgres:16` or `pgvector/pgvector:pg16` locally.\n\n**How do I run psql inside the container?**\n`docker exec -it dev_postgres psql -U devuser -d myapp_dev`\n\n**Does this work on Apple Silicon (M-series Macs)?**\nYes. The official Postgres and pgvector images publish `linux/arm64` variants; Docker Desktop handles the architecture automatically.\n\n## Where to go next\n\nSee [How to write a database migration in 2026](/blog/how-to-write-a-database-migration-2026), [How to add full-text search in 2026](/blog/how-to-add-full-text-search-2026), and [How to cache with Redis in 2026](/blog/how-to-cache-with-redis-2026).","hue":322,"metaTitle":"How to Set Up Postgres Locally 2026","metaDescription":"Set up a local Postgres database in 2026 with Docker Compose. Covers pgvector, dev workflow, connection strings, seed scripts, and GUI tools.","keywords":["how to set up postgres locally 2026","postgres docker compose setup","local postgres development","pgvector local setup","postgres dev environment 2026","docker postgres tutorial","postgres connection string","local database setup","postgres seed script","postgres gui tools 2026"],"tldr":["**Use Docker Compose, not a native install** — portable, version-pinned, and teardown is one command; no leftover system processes.","**Pin the Postgres version** — `postgres:16` in dev must match production; version mismatches cause subtle query plan and behavior differences.","**Add pgvector from the start** if your app does AI/embeddings** — the `pgvector/pgvector` image includes it; adding it later requires a migration.","**Store the connection string in `.env`** and read it with `dotenv`; never hardcode credentials in application config files.","**Skip:** running migrations manually every time. Automate `migrate` as part of your dev startup script so the schema is always up to date."],"readTime":"4 min read","wordCount":850},"related":[{"slug":"how-to-set-up-a-dev-environment-2026","title":"How to set up a dev environment in 2026: tools, containers, and reproducibility","excerpt":"A dev environment that differs from production is a bug factory. This guide covers reproducible local setups with containers, dotfiles, and modern tooling.","category":"code","date":"April 17, 2026","author":"ByteLedger Team","readTime":"5 min read","hue":316},{"slug":"how-to-set-up-a-database-2026","title":"How to set up a database in 2026: the practical guide","excerpt":"Choosing and setting up a database is the first real architecture decision in any new project. Here is the 2026-correct path without wasted effort.","category":"code","date":"April 3, 2026","author":"ByteLedger Team","readTime":"5 min read","hue":321},{"slug":"pgvector-explained-2026","title":"pgvector Explained in 2026: Vector Search in Postgres","excerpt":"pgvector adds a vector type and nearest-neighbor search to Postgres, so RAG and recommendation embeddings live next to your relational data, not apart from it.","category":"code","date":"August 7, 2026","author":"ByteLedger Team","readTime":"4 min read","hue":304}],"sameCategory":[{"slug":"bazel-build-explained-2026","title":"Bazel Explained: What Hermetic Builds Actually Cost","excerpt":"Bazel delivers reproducible, precisely cached builds and demands that you describe everything. Here is what you get, what it costs, and who should say no.","category":"code","date":"August 19, 2026","author":"ByteLedger Team","readTime":"3 min read","hue":145},{"slug":"graphql-federation-2026","title":"GraphQL Federation: One Graph, Many Teams","excerpt":"Federation composes independently-owned subgraphs into a single API. Here is what it solves, the operational cost it adds, and when a simpler gateway is enough.","category":"code","date":"August 19, 2026","author":"ByteLedger Team","readTime":"3 min read","hue":300},{"slug":"hermetic-builds-explained-2026","title":"Hermetic Builds Explained: Same Inputs, Same Output","excerpt":"A hermetic build depends only on declared inputs. Here is what that property gives you, the common leaks that break it, and how to move toward it incrementally.","category":"code","date":"August 19, 2026","author":"ByteLedger Team","readTime":"3 min read","hue":130},{"slug":"monorepo-tooling-compared-2026","title":"Monorepo Tooling Compared: What You Actually Need","excerpt":"Monorepo tools solve task orchestration and caching, not repository layout. Here is what each tier provides and how to tell when you have outgrown the simple option.","category":"code","date":"August 19, 2026","author":"ByteLedger Team","readTime":"3 min read","hue":150}],"newer":{"slug":"how-to-set-up-port-forwarding-2026","title":"How to set up port forwarding in 2026: a practical step-by-step guide","excerpt":"Port forwarding lets you expose a home server, game server, or camera to the internet safely. Here is how to do it correctly in 2026 without opening more than you need.","category":"tech","date":"March 16, 2026","author":"ByteLedger Team","readTime":"5 min read","hue":234},"older":{"slug":"how-to-set-up-prettier-2026","title":"How to set up Prettier in 2026","excerpt":"Prettier ends code-style debates by enforcing a single format on save. This guide covers installation, config, editor integration, and CI enforcement in 2026.","category":"code","date":"March 16, 2026","author":"ByteLedger Team","readTime":"4 min read","hue":306}}