How to Deploy Logto to Production: A Complete Self-Hosting Guide

Deploy Logto to production by configuring environment variables from the .env.example template, running the official docker-compose.yml stack with PostgreSQL, and terminating TLS at a reverse proxy.

Logto is a self-hosted OIDC/OAuth 2.1 identity provider maintained in the logto-io/logto repository. This guide explains how to deploy Logto to production using the official containerization artifacts and configuration patterns found in the source code.

Prerequisites and Host Preparation

Before deploying Logto to production, prepare a Linux host with Docker Engine 20.10+ and Docker Compose v2 installed. Create a dedicated non-root user for container execution to follow security best practices.

Open the following ports on your firewall:

  • 3001 – Core public endpoint (OIDC/OAuth 2.1)
  • 3002 – Core admin API and console
  • 5432 – PostgreSQL database (internal or external)

Configuring Production Environment Variables

Logto exposes all runtime configuration through environment variables parsed in packages/core/src/config.ts. Copy the example file to create your production configuration:

curl -sSL https://raw.githubusercontent.com/logto-io/logto/master/.env.example -o .env

Edit the .env file to set these critical production variables:

Variable Purpose Production Value
DB_URL PostgreSQL connection string postgres://logto:strong-password@db:5432/logto
ADMIN_URL Public URL for the admin console https://admin.example.com
ENDPOINT_URI Public Core URL for OIDC discovery https://auth.example.com
JWT_SECRET HMAC secret for token signing (≥32 bytes) Randomly generated string
SESSION_COOKIE_SECURE Enforce HTTPS-only cookies true
CORS_ALLOWED_ORIGINS Permitted origins for API requests https://app.example.com

The LOGTO_DISABLE_DEVELOPER_MODE=true setting disables development shortcuts and enables strict production validations.

Starting the Logto Production Stack

The repository's docker-compose.yml orchestrates the Core server, PostgreSQL database, and automatic database migrations. From the repository root, execute:

docker compose -p logto -f docker-compose.yml pull
docker compose -p logto -f docker-compose.yml up -d

This compose configuration performs the following actions automatically:

  1. Database initialization – Creates a persistent Docker volume (logto-db-data) for PostgreSQL
  2. Schema migrations – Runs pnpm cli db migrate from packages/schemas/migrations/ on startup
  3. Server startup – Executes pnpm prepack && pnpm start:prod inside the node:20-alpine container
  4. Service exposure – Binds port 3001 for public OIDC endpoints and 3002 for the admin API

The migrations are idempotent, allowing safe restarts and updates without manual intervention.

Enabling TLS and Reverse Proxy

While Logto containers expose plain HTTP internally, production deployments must terminate TLS at a reverse proxy. Configure Nginx to forward traffic to the Core containers:

server {
  listen 443 ssl;
  server_name auth.example.com;

  ssl_certificate     /etc/ssl/certs/example.com.crt;
  ssl_certificate_key /etc/ssl/private/example.com.key;

  location / {
    proxy_pass http://localhost:3001;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
  }
}

Alternatively, use Traefik or Caddy for automatic Let's Encrypt certificate management. Ensure ENDPOINT_URI and ADMIN_URL environment variables reflect your public HTTPS URLs.

Verifying the Production Deployment

Confirm successful deployment by checking the OpenID Connect discovery endpoint:

curl https://auth.example.com/.well-known/openid-configuration

This should return a JSON document containing the OIDC configuration. Access the admin console at your configured ADMIN_URL and authenticate using the initial credentials created during the database seeding phase.

Verify database migrations completed by inspecting the schema version in the PostgreSQL instance; migration scripts reside in packages/schemas/migrations/ and apply automatically when the container starts.

Scaling and Maintenance

Horizontal scaling requires only running additional Core containers behind the same reverse proxy. All instances connect to the shared PostgreSQL database and maintain stateless operation.

Zero-downtime upgrades follow this workflow:

  1. Update the image tag in docker-compose.yml
  2. Run docker compose pull && docker compose up -d
  3. The new container executes migrations automatically before accepting traffic

For custom deployments, build a production image using the repository Dockerfile:

FROM logtoio/logto:latest
COPY .env /app/.env
RUN pnpm prepack
EXPOSE 3001 3002
CMD ["pnpm", "start:prod"]

Summary

  • Configuration – Set production secrets in .env based on the .env.example template, ensuring JWT_SECRET exceeds 32 bytes and SESSION_COOKIE_SECURE is enabled
  • Orchestration – Use the official docker-compose.yml to run PostgreSQL and Logto Core with automatic migrations from packages/schemas/migrations/
  • Security – Terminate TLS at a reverse proxy and restrict CORS_ALLOWED_ORIGINS to known application domains
  • Entry point – The Core server initializes in packages/core/src/app.ts, loading configurations from packages/core/src/config.ts before binding to ports 3001 and 3002

Frequently Asked Questions

What are the minimum system requirements for running Logto in production?

Logto requires a host capable of running Docker containers with at least 2GB RAM and 20GB storage for the initial deployment. PostgreSQL 14+ is required for the database layer, though the docker-compose.yml provides a preconfigured instance. CPU requirements scale with authentication volume; a single Core container handles thousands of requests per minute on standard cloud instance sizes.

How do I handle database migrations when upgrading Logto?

Database migrations run automatically when the Core container starts. The migration scripts in packages/schemas/migrations/ execute sequentially via pnpm cli db migrate before the HTTP server binds to ports 3001 and 3002. Because migrations are idempotent, you can safely perform zero-downtime upgrades by bringing up new containers before terminating old ones.

Can I run Logto without Docker in production?

While possible by executing pnpm start:prod directly on a Node.js 20+ host, the repository strongly recommends containerized deployment. The Dockerfile and docker-compose.yml handle dependency resolution, environment isolation, and migration sequencing that manual deployments must replicate. Running bare-metal requires manually managing PostgreSQL connections and the build process defined in pnpm prepack.

How do I add social connectors like Google in a production deployment?

Install connectors at runtime without rebuilding the image. Execute docker exec -it logto-core pnpm add @logto/connector-google inside the running container, then register the connector via the admin API at port 3002. The connector configuration persists in PostgreSQL, making it available across container restarts and scalable to multiple Core instances without code changes.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →