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 thatDATABASE_URLis a proper PostgreSQL URI. Invalid URLs cause immediate startup failure. - Automatic migrations: The
ensureMigrationsfunction 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_URLto a PostgreSQL connection string to activate external database mode - Use
PAPERCLIP_DEPLOYMENT_MODE=authenticatedfor production security - Adjust
PAPERCLIP_BINDandPAPERCLIP_DEPLOYMENT_EXPOSUREbased 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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →