Skip to main content
Running multiple Prefect server instances enables high availability and distributes load across your infrastructure. This guide covers configuration and deployment patterns for scaling self-hosted Prefect.

Requirements

Multi-server deployments require:
  • PostgreSQL database version 14.9 or higher (SQLite does not support multi-server synchronization)
  • Redis for event messaging
  • Load balancer for API traffic distribution

Architecture

A scaled Prefect deployment typically includes:
  • Multiple API server instances - Handle UI and API requests
  • Background services - Runs the scheduler, automation triggers, and other loop services. Can be scaled horizontally by running multiple instances coordinated through Redis
  • PostgreSQL database - Stores all persistent data and synchronizes state across servers
  • Redis - Distributes events between services and coordinates background service work
  • Load balancer - Routes traffic to healthy API instances (e.g. NGINX or Traefik)

Configuration

Database setup

Configure PostgreSQL as your database backend:
PostgreSQL version 14.9 or higher is required for multi-server deployments. SQLite does not support the features needed for state synchronization across multiple servers.

AWS RDS IAM Authentication

To use AWS IAM authentication for your PostgreSQL database (experimental):
  1. Install the AWS integration:
  2. Create an IAM policy with rds-db:connect permission and attach it to your IAM user/role.
  3. Enable plugins and IAM authentication:
  4. Configure your connection URL:

Azure Managed Identity Authentication

To use Azure managed identity (Microsoft Entra ID) authentication for your PostgreSQL database (experimental):
  1. Install the Azure integration:
  2. Enable Microsoft Entra authentication on your Azure Database for PostgreSQL flexible server and grant the identity a database principal via the pgaadauth extension.
  3. Enable plugins and managed identity authentication:
  4. Configure your connection URL:

Redis setup

Configure Redis as your server’s message broker, cache, and lease storage:
If your Redis instance requires authentication, you may configure a username and password:
For Redis instances that require an encrypted connection, you can enable SSL/TLS:
Alternatively, configure the Redis connection with a single URL instead of individual fields. When PREFECT_REDIS_MESSAGING_URL is set, it takes precedence and the individual host, port, db, username, password, and SSL fields are ignored:
Use rediss:// for TLS connections:
Redis Cluster mode (redis+cluster:// and rediss+cluster:// URL schemes) is not yet supported. If you configure a cluster URL, Prefect raises an error at startup rather than attempting a partial connection.

Docket URL for background services

Prefect uses Docket to coordinate background services like the scheduler, late run detection, and automation triggers. By default, Docket uses in-memory storage (memory://), which only works for single-server deployments. For high-availability deployments, configure Docket to use Redis:
If your Redis instance requires authentication:
For Redis instances that require SSL/TLS:
The Docket URL can use the same Redis instance as the messaging configuration above, but you may use a different database number (e.g., /1 instead of /0) to keep the data separate.

Service separation

For optimal performance, run API servers and background services separately: API servers (multiple instances):
Background services:
For high-volume deployments, consider reducing the event retention period from the default 7 days to prevent rapid database growth. See database maintenance for configuration details.

Running multiple background services

For high availability and throughput, you can run multiple prefect server services start processes in parallel. Prefect uses Docket (backed by Redis) to coordinate work across background service processes so that periodic work (like scheduling, late run detection, and automation trigger evaluation) runs exactly once per interval even when multiple processes are running. To run multiple background service processes:
  1. Configure PREFECT_SERVER_DOCKET_URL to point at a shared Redis instance (see Docket URL for background services). The default in-memory backend (memory://) is only safe for a single process.
  2. Ensure every background service process connects to the same PostgreSQL database, Redis messaging instance, and Docket URL as the API servers.
  3. Start one prefect server services start process per replica. Each replica runs the same set of enabled services; Docket ensures only one replica picks up each scheduled run.
Do not run multiple background service processes without configuring PREFECT_SERVER_DOCKET_URL to use Redis. With the default memory:// backend each process schedules its own work independently, which causes duplicate scheduled runs, duplicate automation actions, and other correctness problems.

Running specific services on dedicated processes

All services run together in a single prefect server services start process by default. To dedicate a process to a specific subset of services (for example, to scale a noisy neighbor independently), disable the services you don’t want to run on that process with their *_ENABLED environment variable. List all services and their enable/disable environment variable:
For example, to run a process that only handles the scheduler and late run detection:
Then run another process with the complementary set of services enabled. As long as every enabled service is running on at least one process (and all processes share the same Docket Redis), every enabled service continues to operate.
Some services maintain their own at-least-once semantics at the database or Redis level rather than relying on Docket’s run-once guarantee. Running multiple processes with the same service enabled is supported, but starting with one process per service (and scaling up only when you observe a specific bottleneck) keeps operations simple.

Database migrations

Disable automatic migrations in multi-server deployments:
Run migrations separately before deployment:

Load balancer configuration

Configure health checks for your load balancer:
  • Health endpoint: /api/health (verifies the HTTP server is running)
  • Readiness endpoint: /api/ready (verifies database connectivity and returns 503 when Postgres is unreachable)
  • Expected response: HTTP 200 with JSON true from /api/health
  • Check interval: 5-10 seconds
Use /api/health for liveness-style checks where you only need to confirm the process is up. Use /api/ready when the check should fail if the database is unavailable—for example, to stop routing traffic during a Postgres outage. See Kubernetes health and readiness probes for probe configuration on Kubernetes. Example NGINX configuration:

Reverse proxy configuration

When hosting Prefect behind a reverse proxy, ensure proper header forwarding:

UI proxy settings

When self-hosting the UI behind a proxy:
  • PREFECT_UI_API_URL: Connection URL from UI to API
  • PREFECT_UI_SERVE_BASE: Base URL path to serve the UI
  • PREFECT_UI_URL: URL for clients to access the UI

SSL certificates

For self-signed certificates:
  1. Add certificate to system bundle and set:
  2. Or disable verification (testing only):

Environment proxy settings

Prefect respects standard proxy environment variables:

Deployment examples

Docker Compose

Deploying Prefect self-hosted somehow else? Consider opening a PR to add your deployment pattern to this guide.

Operations

Migration considerations

Handling large databases

When running migrations on large database instances (especially where tables like events, flow_runs, or task_runs can reach millions of rows), the default database timeout of 10 seconds may not be sufficient for creating indexes. If you encounter a TimeoutError during migrations, increase the database timeout:
For Docker deployments:
Index creation time scales with table size. A database with millions of events may require 30+ minutes for some migrations. If a migration fails due to timeout, you may need to manually clean up any partially created indexes before retrying.

Recovering from failed migrations

If a migration times out while creating indexes, you may need to manually complete it. For example, if migration 7a73514ca2d6 fails:
  1. First, check which indexes were partially created:
  2. Manually create the missing indexes using CONCURRENTLY to avoid blocking:
  3. Mark the migration as complete:
Only use manual recovery if increasing the timeout and retrying the migration doesn’t work. Always verify the correct migration version and index definitions from the migration files.

Monitoring

Monitor your multi-server deployment:
  • Database connections: Watch for connection pool exhaustion
  • Redis memory: Ensure adequate memory for message queues
  • API response times: Track latency across different endpoints
  • Background service lag: Monitor time between event creation and processing

Best practices

  1. Start with 2-3 API instances and scale based on load
  2. Use connection pooling to manage database connections efficiently
  3. Monitor extensively before scaling further (e.g. Prometheus + Grafana or Logfire)
  4. Test failover scenarios regularly

Further reading