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

> Deploy Paperclip AI to production with an external PostgreSQL database. Set DATABASE_URL and enable authenticated mode for a seamless setup. Get your AI running efficiently.

- Repository: [Paperclip/paperclip](https://github.com/paperclipai/paperclip)
- Tags: how-to-guide
- Published: 2026-08-12

---

**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](https://github.com/paperclipai/paperclip) repository.

## How Paperclip AI Selects the Database Backend

The server determines which database to use during startup in [`server/src/index.ts`](https://github.com/paperclipai/paperclip/blob/main/server/src/index.ts) (lines 340–350). The `loadConfig()` function from [`server/src/config.ts`](https://github.com/paperclipai/paperclip/blob/main/server/src/config.ts) checks for the presence of `DATABASE_URL`:

```ts
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`](https://github.com/paperclipai/paperclip/blob/main/server/src/config.ts) (lines 300–303), the configuration resolves `databaseUrl` as:

```ts
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`](https://github.com/paperclipai/paperclip/blob/main/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:

```bash
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):**

```bash
pnpm paperclipai start

```

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

**Via Docker:**

```dockerfile
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):**

```yaml
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`](https://github.com/paperclipai/paperclip/blob/main/docs/deploy/aws-ecs.md).

## Safety Checks and Migration Handling

- **URL validation**: [`server/src/index.ts`](https://github.com/paperclipai/paperclip/blob/main/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`](https://github.com/paperclipai/paperclip/blob/main/server/src/config.ts) | Loads configuration and resolves `DATABASE_URL` from environment |
| [`server/src/index.ts`](https://github.com/paperclipai/paperclip/blob/main/server/src/index.ts) | Server bootstrap logic; selects external vs. embedded PostgreSQL |
| [`docs/deploy/overview.md`](https://github.com/paperclipai/paperclip/blob/main/docs/deploy/overview.md) | Deployment modes and external DB recommendations |
| [`docs/deploy/environment-variables.md`](https://github.com/paperclipai/paperclip/blob/main/docs/deploy/environment-variables.md) | Complete environment variable reference |
| [`docs/deploy/aws-ecs.md`](https://github.com/paperclipai/paperclip/blob/main/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.