6.7 KiB
Deployment Guide — Self-Hosted (Unraid + Gitea)
Overview
This app runs as two Docker containers:
- app — Next.js server (port 3000)
- db — PostgreSQL 16 (port 5432)
Required Secrets
All of these must be set in .env.production before starting.
| Variable | What it is | How to get it |
|---|---|---|
DATABASE_URL |
Internal postgres URL | postgresql://postgres:POSTGRES_PASSWORD@db:5432/teressa |
POSTGRES_PASSWORD |
Postgres superuser password | Generate: openssl rand -base64 32 |
SIGNING_JWT_SECRET |
Signs document signing tokens | Generate: openssl rand -base64 32 |
AUTH_SECRET |
NextAuth session encryption | Generate: openssl rand -base64 32 |
AUTH_TRUST_HOST |
Required for reverse proxy | Set to true |
AGENT_EMAIL |
Your login email for the portal | Your email address |
AGENT_PASSWORD |
Your login password for the portal | Choose a strong password |
CONTACT_EMAIL_USER |
SMTP username | See SMTP section below |
CONTACT_EMAIL_PASS |
SMTP password/API key | See SMTP section below |
CONTACT_SMTP_HOST |
SMTP server hostname | See SMTP section below |
CONTACT_SMTP_PORT |
SMTP port | 587 (STARTTLS) or 465 (SSL) |
OPENAI_API_KEY |
OpenAI API key for AI field placement | platform.openai.com → API keys |
APP_BASE_URL |
Public URL of the app (no trailing slash) | e.g. https://teressa.yourdomain.com |
SMTP Options
- Resend (recommended): host=
smtp.resend.com, port=465, user=resend, pass=Resend API key - Gmail: host=
smtp.gmail.com, port=587, user=your Gmail, pass=App Password (not your regular password)
Part 1: Push Repo to Gitea
On your local machine
# Add your Gitea instance as a remote
git remote add gitea http://YOUR_UNRAID_IP:3000/YOUR_GITEA_USERNAME/teressa-copeland-homes.git
# Push
git push gitea main
Create the repo in Gitea first (via the Gitea web UI) before pushing.
Part 2: Build and Push Docker Image to Gitea Registry
Gitea includes a built-in OCI container registry. Replace YOUR_UNRAID_IP, YOUR_GITEA_USERNAME, and YOUR_GITEA_PORT with your values.
# Log in to Gitea's container registry
docker login YOUR_UNRAID_IP:YOUR_GITEA_PORT
# Build the image (from the project root)
docker build -t YOUR_UNRAID_IP:YOUR_GITEA_PORT/YOUR_GITEA_USERNAME/teressa-copeland-homes:latest .
# Push to Gitea registry
docker push YOUR_UNRAID_IP:YOUR_GITEA_PORT/YOUR_GITEA_USERNAME/teressa-copeland-homes:latest
Gitea registry port is usually the same as the Gitea web port (e.g. 3000 or 10880 depending on your setup). Check your Gitea container settings in Unraid.
HTTP registry: If Gitea is HTTP (not HTTPS), add the registry to Docker's insecure registries. On Unraid, go to Settings → Docker → Insecure Registries and add
YOUR_UNRAID_IP:PORT.
Part 3: Deploy on Unraid
Option A: Docker Compose (recommended)
Unraid supports docker-compose via the Compose Manager community app.
- Install Compose Manager from Community Apps if not already installed
- SSH into Unraid and create the app directory:
mkdir -p /mnt/user/appdata/teressa-copeland-homes cd /mnt/user/appdata/teressa-copeland-homes - Copy
docker-compose.ymland.env.production.exampleto this directory:cp docker-compose.yml .env.production.example /mnt/user/appdata/teressa-copeland-homes/ - Create
.env.productionfrom the example and fill in all values:cp .env.production.example .env.production nano .env.production - Set
APP_IMAGEto your Gitea registry URL in.env.production:APP_IMAGE=YOUR_UNRAID_IP:YOUR_GITEA_PORT/YOUR_GITEA_USERNAME/teressa-copeland-homes:latest - In Compose Manager, add the compose file and start it
Option B: Unraid Docker UI (manual containers)
Add two containers via Docker → Add Container:
Container 1 — postgres
- Repository:
postgres:16-alpine - Name:
teressa-db - Port:
5432:5432 - Environment variables:
POSTGRES_USER=postgresPOSTGRES_PASSWORD=YOUR_STRONG_PASSWORDPOSTGRES_DB=teressa
- Path:
/mnt/user/appdata/teressa-db→/var/lib/postgresql/data
Container 2 — app
- Repository:
YOUR_UNRAID_IP:YOUR_GITEA_PORT/YOUR_GITEA_USERNAME/teressa-copeland-homes:latest - Name:
teressa-app - Port:
3000:3000 - Network: Same as teressa-db (so it can reach
teressa-dbby name, or use Unraid IP) - Environment variables: All 13 from the table above
- Note:
DATABASE_URLshould use the Unraid host IP or container name instead ofdb
- Note:
- Path:
/mnt/user/appdata/teressa-uploads→/app/uploads
Part 4: Run Migrations and Seed
These must run after the database container is healthy, before the app is usable.
Run migrations
# From your local machine (requires node + npx)
DATABASE_URL="postgresql://postgres:YOUR_PASSWORD@YOUR_UNRAID_IP:5432/teressa" npx drizzle-kit migrate
# Or SSH into Unraid and run from the project directory
docker exec -it teressa-app npx drizzle-kit migrate
Seed agent account + form templates
# SSH into Unraid
docker exec \
-e DATABASE_URL="postgresql://postgres:YOUR_PASSWORD@db:5432/teressa" \
-e AGENT_EMAIL="your@email.com" \
-e AGENT_PASSWORD="your-password" \
teressa-app npx tsx scripts/seed.ts
# Seed the 140 real estate form templates
docker exec \
-e DATABASE_URL="postgresql://postgres:YOUR_PASSWORD@db:5432/teressa" \
teressa-app npx tsx scripts/seed-forms.ts
Both seed scripts are idempotent — safe to re-run.
Part 5: Verify
curl http://YOUR_UNRAID_IP:3000/api/health
# Expected: {"ok":true,"db":"connected"}
Then open http://YOUR_UNRAID_IP:3000 in a browser and log in with AGENT_EMAIL / AGENT_PASSWORD.
Updating
When you push a new version:
# On local machine — rebuild and push image
docker build -t YOUR_UNRAID_IP:PORT/YOUR_GITEA_USERNAME/teressa-copeland-homes:latest .
docker push YOUR_UNRAID_IP:PORT/YOUR_GITEA_USERNAME/teressa-copeland-homes:latest
# On Unraid — pull new image and restart
docker pull YOUR_UNRAID_IP:PORT/YOUR_GITEA_USERNAME/teressa-copeland-homes:latest
docker restart teressa-app
# If schema changed, run migrations first:
DATABASE_URL="postgresql://postgres:YOUR_PASSWORD@YOUR_UNRAID_IP:5432/teressa" npx drizzle-kit migrate
Reverse Proxy (optional)
If you want HTTPS via a domain name, put a reverse proxy in front on port 443:
- Nginx Proxy Manager (available in Unraid Community Apps) — easiest GUI option
- Caddy — automatic HTTPS with Let's Encrypt
- Traefik — label-based routing
Point the proxy at http://YOUR_UNRAID_IP:3000. Make sure APP_BASE_URL in .env.production matches the public HTTPS URL, and ensure AUTH_TRUST_HOST=true is set.