How to Deploy Paperclip AI to Production with an External PostgreSQL Database

Set the DATABASE_URL environment variable to a valid PostgreSQL connection string and switch PAPERCLIP_DEPLOYMENT_MODE to authenticated to run Paperclip AI in production with an external database.

Paperclip AI is a multi-tenant control-plane that supports both embedded PostgreSQL for local development and external PostgreSQL for production workloads. This guide explains how to configure and deploy Paperclip AI with a production-grade PostgreSQL instance, based on the source code implementation in the paperclipai/paperclip repository.

How Paperclip AI Selects the Database Backend

The server determines which database to use during startup in server/src/index.ts (lines 340–350). The loadConfig() function from server/src/config.ts checks for the presence of DATABASE_URL:

if (config.databaseUrl) {
  // External PostgreSQL supplied → use it
  db = createDb(config.databaseUrl);
  pluginMigrationDb = config.databaseMigrationUrl
    ? createDb(config.databaseMigrationDb)
    : db;
  logger.info("Using external PostgreSQL via DATABASE_URL/config");
  activeDatabaseConnectionString = config.databaseUrl;
  startupDbInfo = { mode: "external-postgres", connectionString: config.databaseUrl };
} else {
  // Fallback to embedded-postgres
  …
}

In server/src/config.ts (lines 300–303), the configuration resolves databaseUrl as:

databaseUrl: process.env.DATABASE_URL ?? fileDbUrl,

If DATABASE_URL is unset, the server falls back to an embedded PostgreSQL instance using the embedded-postgres NPM package. Production deployments must explicitly set DATABASE_URL to avoid the embedded database.

Required Environment Variables for Production

Variable Default Production Value
DATABASE_URL (embedded) postgres://user:pass@host:5432/dbname
PAPERCLIP_DEPLOYMENT_MODE local_trusted authenticated
PAPERCLIP_DEPLOYMENT_EXPOSURE private public (if internet-facing)
PAPERCLIP_BIND loopback lan, tailnet, or custom
PORT 3100 As needed
PAPERCLIP_SECRETS_PROVIDER local Cloud provider (AWS, GCP, etc.)
PAPERCLIP_SECRETS_STRICT_MODE false true (recommended)

All variables are documented in docs/deploy/environment-variables.md.

Step-by-Step Production Deployment Workflow

1. Provision PostgreSQL

Create a PostgreSQL instance using Amazon RDS, Azure Database for PostgreSQL, Google Cloud SQL, or a self-hosted cluster. Ensure network connectivity from the Paperclip host.

2. Construct the Connection String

Format: postgres://<user>:<password>@<host>:<port>/<database>

Example: postgres://paperclip:secret@db.example.com:5432/paperclip

3. Configure Environment Variables

Export or inject the required variables:

export DATABASE_URL="postgres://paperclip:secret@db.example.com:5432/paperclip"
export PAPERCLIP_DEPLOYMENT_MODE=authenticated
export PAPERCLIP_DEPLOYMENT_EXPOSURE=public
export PAPERCLIP_BIND=lan
export PORT=3100

4. Start the Server

Via CLI (with pnpm):

pnpm paperclipai start

The server logs "Using external PostgreSQL via DATABASE_URL/config" to confirm the external database is active.

Via Docker:

FROM node:24-alpine AS runtime
WORKDIR /app
COPY . .
RUN pnpm install --prod

ENV NODE_ENV=production
ENV DATABASE_URL=${DATABASE_URL}
ENV PAPERCLIP_DEPLOYMENT_MODE=authenticated
ENV PAPERCLIP_DEPLOYMENT_EXPOSURE=public
ENV PAPERCLIP_BIND=lan

CMD ["node", "dist/server/src/index.js"]

Via AWS ECS (task definition fragment):

Environment:
  - Name: DATABASE_URL
    Value: "postgres://paperclip:secret@db.example.com:5432/paperclip"
  - Name: PAPERCLIP_DEPLOYMENT_MODE
    Value: "authenticated"
  - Name: PAPERCLIP_DEPLOYMENT_EXPOSURE
    Value: "public"
  - Name: PAPERCLIP_BIND
    Value: "lan"

The full production deployment guide is available in docs/deploy/aws-ecs.md.

Safety Checks and Migration Handling

  • URL validation: server/src/index.ts (lines 251–256) validates that DATABASE_URL is a proper PostgreSQL URI. Invalid URLs cause immediate startup failure.
  • Automatic migrations: The ensureMigrations function applies schema migrations on startup using the configured connection string, ensuring the database schema is always current.

Key Source Files for Production Deployment

File Purpose
server/src/config.ts Loads configuration and resolves DATABASE_URL from environment
server/src/index.ts Server bootstrap logic; selects external vs. embedded PostgreSQL
docs/deploy/overview.md Deployment modes and external DB recommendations
docs/deploy/environment-variables.md Complete environment variable reference
docs/deploy/aws-ecs.md Production cloud deployment patterns
Dockerfile Container image for production deployments

Summary

  • Set DATABASE_URL to a PostgreSQL connection string to activate external database mode
  • Use PAPERCLIP_DEPLOYMENT_MODE=authenticated for production security
  • Adjust PAPERCLIP_BIND and PAPERCLIP_DEPLOYMENT_EXPOSURE based on network requirements
  • Validate deployment by checking server logs for "Using external PostgreSQL via DATABASE_URL/config"
  • Migrations run automatically on startup; no manual schema management required

Frequently Asked Questions

What happens if I don't set DATABASE_URL?

The server falls back to embedded PostgreSQL via the embedded-postgres package. This is suitable for local development only and should never be used in production.

Does Paperclip AI support connection pooling or read replicas?

The source code shows DATABASE_MIGRATION_URL can be configured separately from DATABASE_URL for migration operations. For connection pooling, configure your PostgreSQL URI with pooler parameters or use an external pooler like PgBouncer.

How do I verify the external database is being used?

Check the server logs on startup. The message "Using external PostgreSQL via DATABASE_URL/config" confirms external mode. Additionally, startupDbInfo.mode will equal "external-postgres".

Can I use a secrets manager instead of DATABASE_URL in plain text?

Yes. Set PAPERCLIP_SECRETS_PROVIDER to aws, gcp, or another supported provider, then reference the secret in your configuration. Enable PAPERCLIP_SECRETS_STRICT_MODE=true to enforce secret references for sensitive values.

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 →