- Add @project-e/db package with Drizzle schema and migrations - Replace PocketBase client with PostgreSQL-based database client - Migrate auth from custom to NextAuth.js - Add Docker Compose with PostgreSQL container - Update worker to use new database client - Remove PocketBase-specific files and migrations - Add drizzle config and initial migration
5.9 KiB
Deployment Guide
Deploy Project E with Docker Compose and a reverse proxy such as Nginx Proxy Manager. Compose runs three services: the Next.js web app, PostgreSQL 16, and the background worker.
Prerequisites
- Docker 24.0 or later
- Docker Compose 2.20 or later
- A reverse proxy for TLS and public routing
- At least 1 GB RAM and 10 GB disk space
Deploy Project E
-
Clone the repository on the server.
git clone <repository-url> cd ProjectE -
Create the environment file.
cp .env.example .env -
Set the required values in
.env.POSTGRES_PASSWORD=your_postgres_password DATABASE_URL=postgresql://project_e:your_postgres_password@localhost:5432/project_e NEXTAUTH_SECRET=your_long_random_secret INITIAL_ADMIN_EMAIL=admin@example.com INITIAL_ADMIN_PASSWORD=your_initial_admin_password PUBLIC_URL=https://project-e.example.com COOKIE_SECURE=true ALLOWED_HOSTS=project-e.example.comGenerate secrets with
openssl rand -base64 32. Use the samePOSTGRES_PASSWORDinDATABASE_URL. The web and worker containers use an internal database URL that Compose builds fromPOSTGRES_PASSWORD. -
Build and start the services.
docker compose up -d -
Confirm that the services are healthy.
docker compose ps -
Route your domain to
project-e-web:3000through your reverse proxy. -
Open the app and sign in with
INITIAL_ADMIN_EMAILandINITIAL_ADMIN_PASSWORD. Project E creates that account only when the database contains no users.
Environment variables
| Variable | Required | Description |
|---|---|---|
DATABASE_URL |
Yes | PostgreSQL connection string for local tools and processes outside Compose. |
POSTGRES_PASSWORD |
Yes | Password for the project_e PostgreSQL user. |
NEXTAUTH_SECRET |
Yes | Secret used to sign NextAuth JWT sessions. |
INITIAL_ADMIN_EMAIL |
Yes | Email address for the first account. |
INITIAL_ADMIN_PASSWORD |
Yes | Password for the first account. |
PUBLIC_URL |
No | Public application URL. Defaults to http://localhost:3000. |
COOKIE_SECURE |
No | Set to true behind HTTPS. Defaults to false. |
ALLOWED_HOSTS |
No | Comma-separated allowed hostnames. |
Keep .env out of version control. Rotate NEXTAUTH_SECRET only when you intend to end active sessions.
PostgreSQL and Drizzle
The db service runs postgres:16-alpine, stores its data in the project-e-pg-data Docker volume, and exposes port 5432. The web and worker services connect to db:5432 over the Compose network.
Docker mounts drizzle/0000_first_mauler.sql into PostgreSQL's initialization directory. PostgreSQL runs that file only while it initializes an empty data volume. For a later Drizzle migration, apply the reviewed SQL as part of your release process. Do not expect a container restart to apply a new migration to an existing volume.
To apply a migration from the host, run:
docker compose exec -T db psql -U project_e -d project_e < drizzle/<migration>.sql
Back up the database before applying schema changes.
Nginx Proxy Manager
-
In Nginx Proxy Manager, open Hosts > Proxy Hosts > Add Proxy Host.
-
Set the domain name, choose
http, set the forward hostname toproject-e-web, and set the forward port to3000. -
Select a certificate and force SSL for HTTPS deployments.
-
Add the following advanced configuration to support Server-Sent Events:
proxy_http_version 1.1; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header Host $host; proxy_buffering off; proxy_cache off; proxy_read_timeout 300s; proxy_connect_timeout 300s; proxy_send_timeout 300s; -
Save the host and open the configured domain. You should reach the Project E sign-in page.
Back up and restore PostgreSQL
Create a logical backup without stopping the database:
mkdir -p backups
Restore a backup into a new or empty database:
docker compose exec -T db psql -U project_e -d project_e < backups/project-e-YYYYMMDD_HHMMSS.sql
Back up the project-e-web-uploads volume if your deployment stores uploads there:
docker run --rm \
-v project-e-web-uploads:/source:ro \
-v $(pwd)/backups:/backup \
alpine \
tar czf /backup/uploads-$(date +%Y%m%d).tar.gz -C /source .
Test restores on a separate environment before relying on a backup.
Monitoring and logs
Check service status and logs:
docker compose ps
The web service exposes GET /api/health on port 3000. Configure uptime monitoring for https://your-domain.example/api/health.
Troubleshooting
| Problem | Fix |
|---|---|
| Database connection fails | Confirm that db is healthy and that POSTGRES_PASSWORD matches the value in DATABASE_URL. |
| The web service will not start | Set NEXTAUTH_SECRET, INITIAL_ADMIN_EMAIL, and INITIAL_ADMIN_PASSWORD in .env, then restart the service. |
| Sign-in fails | Confirm the email and password match the initial-admin values. Those values create an account only before any user exists. |
| A schema change is missing | Apply the generated Drizzle SQL. PostgreSQL initialization scripts do not rerun for an existing volume. |
| Realtime updates stop | Disable proxy buffering and set a read timeout of at least 300 seconds. |
| Disk space runs low | Inspect project-e-pg-data and project-e-web-uploads, back up data, and expand the host disk. |
Scaling
The default deployment runs one web service, one worker, and one PostgreSQL instance. Add CPU and memory before changing the topology. Multiple web services require shared session-aware infrastructure and a reverse proxy that supports long-lived SSE connections. Multiple workers coordinate through the PostgreSQL-backed job records.