Rewrite docs/USER_GUIDE.md, docs/DEPLOYMENT.md, docs/DEPLOY_FROM_SCRATCH.md, and DEPLOYMENT_CHECKLIST.md to drop AI-generated tells: bold-colon feature walls, Title Case headings, em dashes, and rule-of-three lists. Facts (commands, paths, ports, URLs) are unchanged. Historical scratch docs (PHOTO_VIEWER_*, FIX_*, *_REVIEW, *_PLAN, etc.) and the cPanel-specific runbooks are untouched.
11 KiB
Deployment
Deploy PunimTag to development or production. Homelab host and Caddy details live in the private ansible repo.
Last reviewed: January 6, 2026.
Table of contents
- Prerequisites
- Development server deployment
- Production deployment
- Environment configuration
- Service management
- Troubleshooting
Prerequisites
Server requirements
- Linux (Ubuntu 20.04+ recommended)
- Python 3.12 or higher
- Node.js 18+ and npm 9+
- PostgreSQL 12+ (required)
- Redis 6+ (required for background jobs)
- At least 4GB RAM (8GB+ recommended for large photo collections)
- Enough disk space for photos and the database
Development server information
Development server: host <backend-host>, user appuser. Contact the
administrator for the password.
Development database: host <db-host>, port 5432, user <db-user>.
Contact the administrator for the password.
Development server deployment
Step 1: build applications
On your local machine or CI/CD pipeline:
# Install dependencies (if not already done)
npm run install:all
# Build all frontends
npm run build:all
# Or use the deployment script
npm run deploy:dev
This builds admin-frontend and viewer-frontend for production and prepares the deployment package.
Step 2: transfer files to server
# Create deployment directory structure
ssh appuser@<backend-host> "mkdir -p /opt/punimtag/{backend,admin-frontend,viewer-frontend,data/uploads,logs}"
# Transfer backend
scp -r backend/* appuser@<backend-host>:/opt/punimtag/backend/
scp requirements.txt appuser@<backend-host>:/opt/punimtag/
# Transfer built frontends
scp -r admin-frontend/dist/* appuser@<backend-host>:/opt/punimtag/admin-frontend/
scp -r viewer-frontend/.next/* appuser@<backend-host>:/opt/punimtag/viewer-frontend/
# Transfer configuration files
scp .env.example appuser@<backend-host>:/opt/punimtag/.env
Step 3: server setup
SSH into the development server:
ssh appuser@<backend-host>
cd /opt/punimtag
Install system dependencies
# Update package list
sudo apt update
# Install PostgreSQL client (if not already installed)
sudo apt install -y postgresql-client
# Install Redis (if not already installed)
sudo apt install -y redis-server
sudo systemctl start redis-server
sudo systemctl enable redis-server
# Install Python dependencies
python3 -m venv venv
source venv/bin/activate
pip install -r requirements.txt
Configure environment
Edit the .env file:
nano .env
Set the following variables:
# Development Database
DATABASE_URL=postgresql+psycopg2://<db-user>:[PASSWORD]@<db-host>:5432/punimtag
DATABASE_URL_AUTH=postgresql+psycopg2://<db-user>:[PASSWORD]@<db-host>:5432/punimtag_auth
# JWT Secrets (change in production!)
SECRET_KEY=dev-secret-key-change-in-production
ADMIN_USERNAME=admin
ADMIN_PASSWORD=<choose-a-strong-password>
# Photo storage
PHOTO_STORAGE_DIR=/opt/punimtag/data/uploads
# Redis
REDIS_URL=redis://localhost:6379/0
# API Configuration
API_HOST=0.0.0.0
API_PORT=8000
Configure frontend environment
Admin frontend: create admin-frontend/.env:
VITE_API_URL=http://<backend-host>:8000
Viewer frontend: create viewer-frontend/.env:
DATABASE_URL=postgresql://<db-user>:[PASSWORD]@<db-host>:5432/punimtag
DATABASE_URL_AUTH=postgresql://<db-user>:[PASSWORD]@<db-host>:5432/punimtag_auth
NEXTAUTH_URL=http://<backend-host>:3001
NEXTAUTH_SECRET=dev-secret-key-change-in-production
Generate Prisma client:
cd viewer-frontend
npx prisma generate
cd ..
Step 4: set up systemd services
Create systemd service files for automatic startup:
Backend API service
sudo nano /etc/systemd/system/punimtag-api.service
[Unit]
Description=PunimTag API Server
After=network.target postgresql.service redis.service
[Service]
Type=simple
User=appuser
WorkingDirectory=/opt/punimtag
Environment="PATH=/opt/punimtag/venv/bin"
Environment="PYTHONPATH=/opt/punimtag"
ExecStart=/opt/punimtag/venv/bin/uvicorn backend.app:app --host 0.0.0.0 --port 8000
Restart=always
RestartSec=10
[Install]
WantedBy=multi-user.target
RQ worker service
sudo nano /etc/systemd/system/punimtag-worker.service
[Unit]
Description=PunimTag RQ Worker
After=network.target postgresql.service redis.service
[Service]
Type=simple
User=appuser
WorkingDirectory=/opt/punimtag
Environment="PATH=/opt/punimtag/venv/bin"
Environment="PYTHONPATH=/opt/punimtag"
ExecStart=/opt/punimtag/venv/bin/python -m rq worker --url redis://localhost:6379/0
Restart=always
RestartSec=10
[Install]
WantedBy=multi-user.target
Admin frontend service (using PM2 or nginx)
For production, use nginx to serve the built frontend. Alternatively, use PM2:
npm install -g pm2
cd /opt/punimtag/admin-frontend
pm2 serve dist 3000 --name punimtag-admin
pm2 save
pm2 startup
Viewer frontend service
cd /opt/punimtag/viewer-frontend
pm2 start npm --name punimtag-viewer -- start
pm2 save
Step 5: start services
# Enable and start services
sudo systemctl enable punimtag-api
sudo systemctl enable punimtag-worker
sudo systemctl start punimtag-api
sudo systemctl start punimtag-worker
# Check status
sudo systemctl status punimtag-api
sudo systemctl status punimtag-worker
Step 6: verify deployment
- Check API health:
curl http://<backend-host>:8000/api/v1/health - Check API documentation: open
http://<backend-host>:8000/docsin a browser. - Access the admin frontend: open
http://<backend-host>:3000. - Access the viewer frontend: open
http://<backend-host>:3001.
Production deployment
Additional production considerations
Security: change all default passwords, use strong JWT secrets, enable HTTPS with SSL certificates, configure firewall rules, restrict database access, and use read-only database users where possible.
Performance: use nginx as a reverse proxy, enable gzip compression, configure caching headers, use a CDN for static assets if you want one, set up database connection pooling, and configure Redis persistence.
Monitoring: set up log aggregation, configure health check endpoints, set up alerting for service failures, and monitor database performance and API response times.
Backup: automate database backups, photo storage backups, and configuration file backups, and have a disaster recovery plan.
Nginx configuration example
# /etc/nginx/sites-available/punimtag
# Admin Frontend
server {
listen 80;
server_name admin.punimtag.example.com;
root /opt/punimtag/admin-frontend/dist;
index index.html;
location / {
try_files $uri $uri/ /index.html;
}
location /api {
proxy_pass http://127.0.0.1:8000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
# Viewer Frontend
server {
listen 80;
server_name viewer.punimtag.example.com;
location / {
proxy_pass http://127.0.0.1:3001;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
# API (optional, if exposing directly)
server {
listen 80;
server_name api.punimtag.example.com;
location / {
proxy_pass http://127.0.0.1:8000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
Environment configuration
Required environment variables
Backend (.env):
# Database (PostgreSQL - required)
DATABASE_URL=postgresql+psycopg2://user:password@host:5432/punimtag
DATABASE_URL_AUTH=postgresql+psycopg2://user:password@host:5432/punimtag_auth
# JWT Configuration
SECRET_KEY=your-secret-key-here
ADMIN_USERNAME=admin
ADMIN_PASSWORD=secure-password
# Photo Storage
PHOTO_STORAGE_DIR=/path/to/photos
# Redis
REDIS_URL=redis://localhost:6379/0
# API Configuration
API_HOST=0.0.0.0
API_PORT=8000
Admin frontend (admin-frontend/.env):
VITE_API_URL=http://your-api-url:8000
Viewer frontend (viewer-frontend/.env):
DATABASE_URL=postgresql://user:password@host:5432/punimtag
DATABASE_URL_AUTH=postgresql://user:password@host:5432/punimtag_auth
NEXTAUTH_URL=http://your-viewer-url:3001
NEXTAUTH_SECRET=your-nextauth-secret
Service management
Systemd commands
# Start services
sudo systemctl start punimtag-api
sudo systemctl start punimtag-worker
# Stop services
sudo systemctl stop punimtag-api
sudo systemctl stop punimtag-worker
# Restart services
sudo systemctl restart punimtag-api
sudo systemctl restart punimtag-worker
# Check status
sudo systemctl status punimtag-api
sudo systemctl status punimtag-worker
# View logs
sudo journalctl -u punimtag-api -f
sudo journalctl -u punimtag-worker -f
PM2 commands
# Start services
pm2 start punimtag-admin
pm2 start punimtag-viewer
# Stop services
pm2 stop punimtag-admin
pm2 stop punimtag-viewer
# Restart services
pm2 restart punimtag-admin
pm2 restart punimtag-viewer
# View logs
pm2 logs punimtag-admin
pm2 logs punimtag-viewer
# Monitor
pm2 monit
Troubleshooting
Common issues
Database connection errors: verify PostgreSQL is running, check the
connection string in .env, verify the database exists and the user has
permissions, and check firewall rules.
Redis connection errors: verify Redis is running (redis-cli ping), check
the Redis URL in .env, and verify Redis is reachable.
API not starting: check logs (sudo journalctl -u punimtag-api -n 50),
verify the Python virtual environment is activated, check that PYTHONPATH
is set correctly, and verify all dependencies are installed.
Frontend build errors: clear node_modules and reinstall, check the
Node.js version (18+), and verify all environment variables are set.
Worker not processing jobs: check worker logs
(sudo journalctl -u punimtag-worker -f), verify the Redis connection,
check the job queue (redis-cli), and restart the worker service.
Log locations
- Systemd logs:
sudo journalctl -u <service-name> - PM2 logs:
pm2 logs - Application logs:
/opt/punimtag/logs/(if configured)
Health checks
# API health
curl http://localhost:8000/api/v1/health
# Redis
redis-cli ping
# PostgreSQL
psql -h <db-host> -U <db-user> -d punimtag -c "SELECT 1;"
Deployment checklist
- Build all frontends
- Transfer files to server
- Install system dependencies
- Set up Python virtual environment
- Configure environment variables
- Set up systemd services
- Configure nginx (production)
- Set up SSL certificates (production)
- Configure firewall rules
- Test all services
- Set up monitoring
- Configure backups
- Document deployment process
For additional help, see README.md (main documentation),
docs/ARCHITECTURE.md (system architecture), and docs/USER_GUIDE.md
(user guide).