The "Database system is starting up" error occurs when attempting to connect to PostgreSQL while it is still initializing after being started. This is a temporary state that occurs during normal startup, especially after crashes or during recovery.
Impact
This error temporarily prevents database connections, causing brief application unavailability during PostgreSQL startup. It's normal during restarts and usually resolves within seconds to minutes.
Common Causes
- PostgreSQL is still starting up
- Database recovery in progress
- Crash recovery running
- Large shared_buffers initialization
- WAL replay during startup
- Extension initialization
- Very large database taking time to initialize
Troubleshooting and Resolution Steps
Wait for startup to complete:
# Check PostgreSQL status sudo systemctl status postgresql # Watch logs for startup progress sudo tail -f /var/log/postgresql/postgresql-15-main.log # Look for "database system is ready to accept connections"Implement connection retry logic:
# Python with exponential backoff import psycopg2 import time def connect_with_retry(max_attempts=10, initial_delay=0.5): for attempt in range(max_attempts): try: conn = psycopg2.connect( "host=localhost dbname=mydb user=myuser password=pass" ) print("Connected successfully") return conn except psycopg2.OperationalError as e: if 'starting up' in str(e).lower(): if attempt < max_attempts - 1: delay = initial_delay * (2 ** attempt) print(f"Database starting up, waiting {delay}s...") time.sleep(delay) continue raise raise Exception("Could not connect after maximum attempts") conn = connect_with_retry()Check startup progress:
-- Once connected, check recovery status SELECT pg_is_in_recovery(); -- Check for active recovery processes SELECT * FROM pg_stat_wal_receiver; -- View startup progress in logs -- Look for messages like: -- "database system was shut down at..." -- "redo starts at..." -- "database system is ready to accept connections"Application connection pooler configuration:
# PgBouncer config - handle startup gracefully [pgbouncer] server_connect_timeout = 30 server_check_delay = 10 server_check_query = SELECT 1 server_lifetime = 3600 server_idle_timeout = 600Docker/Kubernetes health checks:
# Docker Compose services: postgres: image: postgres:15 healthcheck: test: ["CMD-SHELL", "pg_isready -U postgres"] interval: 10s timeout: 5s retries: 5 start_period: 60s # Allow time for startup # Kubernetes livenessProbe: exec: command: - /bin/sh - -c - pg_isready -U postgres initialDelaySeconds: 30 periodSeconds: 10 timeoutSeconds: 5 failureThreshold: 6Check for crash recovery:
# Look for crash recovery messages in logs sudo grep -i "crash\|recovery\|checkpoint" /var/log/postgresql/postgresql-15-main.log # Check for unclean shutdown # If database was not shut down cleanly, recovery will runOptimize startup time:
-- Reduce shared_buffers if startup is slow -- In postgresql.conf shared_buffers = 1GB -- Lower value starts faster -- Disable unnecessary extensions that slow startup -- Comment out in postgresql.conf # shared_preload_libraries = 'pg_stat_statements' # If not needed -- Reload config (requires restart for these settings) SELECT pg_reload_conf();Monitor startup time:
# Time PostgreSQL startup time sudo systemctl restart postgresql # Check logs for startup duration sudo grep "database system is ready" /var/log/postgresql/postgresql-15-main.logApplication-level health check:
# Simple health check endpoint from flask import Flask, jsonify import psycopg2 app = Flask(__name__) @app.route('/health') def health(): try: conn = psycopg2.connect( "host=localhost dbname=mydb", connect_timeout=5 ) cursor = conn.cursor() cursor.execute("SELECT 1") cursor.close() conn.close() return jsonify({"status": "healthy"}), 200 except psycopg2.OperationalError as e: if 'starting up' in str(e).lower(): return jsonify({"status": "starting"}), 503 return jsonify({"status": "unhealthy", "error": str(e)}), 503Graceful degradation:
# Handle startup gracefully in application class DatabaseConnection: def __init__(self): self.conn = None self.retry_delay = 1 def connect(self): if self.conn: return self.conn try: self.conn = psycopg2.connect(...) self.retry_delay = 1 # Reset delay on success return self.conn except psycopg2.OperationalError as e: if 'starting up' in str(e).lower(): print("Database starting, will retry...") time.sleep(self.retry_delay) self.retry_delay = min(self.retry_delay * 2, 30) return None raise # Usage db = DatabaseConnection() while not db.connect(): # Serve cached data or show maintenance page pass
Additional Information
- Normal startup usually completes in seconds
- Crash recovery can take minutes for large databases
- Retry logic is essential for production applications
- Health checks should account for startup time
- Monitor startup logs for issues
- Large shared_buffers increases startup time slightly
- Extensions in shared_preload_libraries affect startup time
Frequently Asked Questions
Q: How long should I wait for PostgreSQL to start?
A: Normal startup: 1-10 seconds. Crash recovery: seconds to minutes depending on database size and activity since last checkpoint.
Q: Is this error normal during restart?
A: Yes, completely normal. PostgreSQL needs time to initialize before accepting connections.
Q: How do I make startup faster?
A: Reduce shared_buffers, minimize shared_preload_libraries, ensure clean shutdowns, regular checkpoints.
Q: Should my application crash if it sees this error?
A: No, implement retry logic with exponential backoff. This is a temporary state.
Q: How do I know when startup is complete?
A: Check logs for "database system is ready to accept connections" or use pg_isready command.
Q: Can I connect as superuser during startup?
A: No, nobody can connect until startup completes, not even superusers.
Q: What if startup takes more than 5 minutes?
A: Check logs for errors, disk I/O issues, or extensive crash recovery. May indicate problems.
Q: How do containers handle this?
A: Use health checks with initialDelaySeconds and retries to wait for startup before marking container ready.