Prisma: Can't reach database server at database:5432 on M1
The PrismaClientInitializationError P1001 on M1 Macs is usually a race condition. Add ?connect_timeout=300 to your DATABASE_URL and verify with pg_isready.
TL;DR#
If you see Can't reach database server at 'database':'5432' after moving your Docker Compose setup to an Apple Silicon Mac, the root cause is almost always a race condition: Prisma tries to connect before PostgreSQL finishes starting. Fix it by appending ?connect_timeout=300 to your DATABASE_URL. If that doesn't work, switch to Node 17 or 18 inside your container.
The error, decoded#
A developer on Stack Overflow reported this exact symptom after switching to an M1 Mac. Their docker-compose.yml defined a PostgreSQL service named test-postgres and a Next.js app using Prisma. The DATABASE_URL pointed to postgres://postgres:postgres@localhost:15432/postgres — note the host localhost and the mapped port 15432. When they ran docker-compose run --publish 5555:5555 next npx prisma migrate dev, Prisma threw:
Can't reach database server at `test-postgres`:`5432`The same error appeared when the app tried to connect at runtime. The PostgreSQL container was running (visible in Docker Desktop), but the Prisma client inside the next container couldn't establish a TCP connection to the database hostname. The container logs also showed:
ERROR: relation "_prisma_migrations" does not exist at character 126That secondary error is a red herring — it means Prisma never got far enough to create the migrations table because the initial connection failed.
The setup is typical: two services in a Compose file, the app referencing the database by its service name (test-postgres), and a port mapping from 15432:5432 on the host. On Intel Macs and Linux, this works. On M1, it breaks.
Why M1 Macs break Docker networking for Prisma and PostgreSQL#
Docker Desktop on Apple Silicon runs inside a lightweight Linux VM (using QEMU or Apple's Virtualization framework). The default bridge network and DNS resolution work, but the startup sequence is slower than on bare-metal Linux. PostgreSQL, especially when pulled as a multi-architecture image, may take a few extra seconds to initialize its data directory and begin accepting connections. Prisma, on the other hand, attempts to connect immediately when prisma migrate dev or the app starts.
The error Can't reach database server at 'database':'5432' is a Prisma-level timeout. Under the hood, Prisma uses the pg driver (or its own engine) to open a TCP socket. If the remote host isn't listening yet, the connection is refused, and Prisma retries a few times before giving up. On M1, the window between "container started" and "PostgreSQL ready" is wider, so the default retry window isn't enough.
The localhost hostname in the original DATABASE_URL is also a problem. Inside a container, localhost refers to the container itself, not the Docker host. The developer had mapped port 15432 on the host to 5432 in the PostgreSQL container, but the app container can't reach the host's localhost unless you use host.docker.internal (which requires Docker Desktop 4.x+ and may need extra configuration on M1). The correct approach is to use the Compose service name (test-postgres) and the container port (5432), because both containers are on the same Compose network.
The accepted answer on Stack Overflow (81 upvotes) identified the fix: add ?connect_timeout=300 to the connection string. This tells Prisma to wait up to 300 seconds for the database to become available, giving PostgreSQL enough time to finish starting. Other answers pointed to Node.js version differences: Node 16 on ARM sometimes exhibits slower DNS resolution or different socket behavior, and upgrading to Node 17 or 18 resolved the issue for some users.
The fix: Add a connection timeout to DATABASE_URL#
The minimal change is to append ?connect_timeout=300 to your DATABASE_URL. If you're using the service name, the URL should look like this:
# Before (fails on M1)
DATABASE_URL="postgres://postgres:postgres@test-postgres:5432/postgres"
# After (works)
DATABASE_URL="postgres://postgres:postgres@test-postgres:5432/postgres?connect_timeout=300"If you were mistakenly using localhost and a mapped port, switch to the service name and the internal port:
# Wrong: localhost inside a container points to the container itself
DATABASE_URL="postgres://postgres:postgres@localhost:15432/postgres"
# Correct: use the Compose service name and the container port
DATABASE_URL="postgres://postgres:postgres@test-postgres:5432/postgres?connect_timeout=300"Update your docker-compose.yml to ensure the app depends on the database and uses the correct environment variable:
services:
postgres:
container_name: 'test-postgres'
restart: unless-stopped
image: 'postgres:13'
ports:
- '15432:5432'
volumes:
- 'pgdata:/var/lib/postgresql/data/'
environment:
POSTGRES_PASSWORD: postgres
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 5s
timeout: 5s
retries: 5
next:
build: .
ports:
- '3000:3000'
depends_on:
postgres:
condition: service_healthy
environment:
DATABASE_URL: "postgres://postgres:postgres@test-postgres:5432/postgres?connect_timeout=300"The healthcheck and depends_on with condition: service_healthy ensure the next container doesn't start until PostgreSQL is actually accepting connections. This eliminates the race condition entirely, even without the timeout parameter. However, keeping connect_timeout=300 adds an extra safety net.
Alternative fix: Upgrade Node.js#
If the timeout alone doesn't resolve the issue, change the Node.js version in your Dockerfile. Several developers on the Stack Overflow thread reported that moving from Node 16 to 17 or 18 fixed the connection error. Update your Dockerfile:
# Before
FROM node:16
# After
FROM node:18Rebuild the image with docker-compose build --no-cache and restart the stack. The newer Node version handles DNS resolution and socket creation differently on ARM, which can sidestep the underlying networking quirk.
If you're using Next.js, make sure your Prisma client is only instantiated in server components. The DATABASE_URL should never be exposed to the browser. See the Server and Client Boundary guide for how to keep data-fetching logic on the server.
Verify the fix#
After applying the changes, bring the stack up:
docker-compose down -v # clean slate
docker-compose up -dCheck that PostgreSQL is healthy:
docker-compose exec postgres pg_isready -U postgresExpected output:
/var/run/postgresql:5432 - accepting connectionsNow run the Prisma migration from the app container:
docker-compose exec next npx prisma migrate devYou should see the migration apply without the Can't reach database server error. The app logs should show a successful database connection.
If you need to test raw connectivity from the app container, use nc (netcat):
docker-compose exec next sh -c "nc -zv test-postgres 5432"Expected output:
test-postgres (172.18.0.2:5432) openOnce the connection is established, you can verify by running a simple query using psql (if you need to exit psql, see How to Exit psql: \q, exit, quit and Ctrl+D).
Two patterns that still trip you up#
Pattern 1: Using localhost instead of the service name#
Inside a Docker container, localhost is the container's own loopback interface. It does not resolve to the host machine or to another container. If your DATABASE_URL uses localhost, Prisma will try to connect to a PostgreSQL instance inside the same container, which doesn't exist. Always use the Compose service name (e.g., test-postgres) and the container port (5432). The host port mapping (15432:5432) is only for external access from your Mac.
Pattern 2: Not waiting for PostgreSQL to be ready#
Even with connect_timeout=300, if your app starts before the database container is created, you'll still see the error. The depends_on with condition: service_healthy is the most reliable way to sequence startup. Without it, Docker only waits for the container to start, not for the service inside to be ready. Combine the healthcheck with the timeout for a bulletproof setup.
If you're using Supabase as your database provider, you might also run into connection timeouts; check out Why Your Supabase Queries Are Slow (And How to Fix) for performance tuning. When you're ready to run migrations, ensure your schema doesn't have foreign key issues that could cause constraint violations; see Fix Foreign Key Constraint Violation in Supabase (23503) for a common pitfall.
FAQ#
Why does Prisma fail with 'Can't reach database server at database:5432' on M1 Macs?#
On Apple Silicon, Docker Desktop runs inside a VM and PostgreSQL initialization can be slower. Prisma attempts to connect before the database is ready, causing a P1001 error. Adding ?connect_timeout=300 to the DATABASE_URL gives Prisma more time to retry and usually resolves the issue.
Does changing the Node.js version fix the Prisma connection error on M1?#
Yes, some developers have resolved the error by upgrading from Node 16 to Node 17 or 18. The newer versions handle DNS resolution and networking differently inside Docker on ARM, which can eliminate the connection timeout.
Can I use host.docker.internal instead of the service name?#
host.docker.internal resolves to the host machine from inside a container. It works on Docker Desktop for Mac, but on M1 it may require Docker Desktop 4.x+ and the --add-host flag in some configurations. Using the Compose service name is simpler and more portable across environments.
How do I test if my database is reachable from inside the container?#
Use docker-compose exec <service> pg_isready -U postgres for PostgreSQL, or nc -zv <host> <port> for a generic TCP check. Both commands confirm whether the database is listening before Prisma tries to connect.
Related#
Frequently Asked Questions
One email a month — no fluff
RLS gotchas, Next.js cache debugging, and the one Supabase setting that bit me last month.
Continue Reading
Fix PostgreSQL Server Won't Start on Mac OS X (2026)
A fresh Homebrew install of PostgreSQL on a Mac completes without errors, then psql answers connection refused because the server was never started. Which command starts it depends on
How to Get Enums in Prisma Client: Import, Query
Stuck trying to access Prisma enum values? This guide shows you exactly how to import, use, and validate generated enum types in your Node.js app.
Extract date (yyyy/mm/dd) from a timestamp in PostgreSQL
Confused by extracting date from a timestamp in PostgreSQL? Learn how to cast to ::date, use date_trunc, and verify column types for straightforward table insertion.
Browse by Topic
Find stories that matter to you.
