Back to Blog
Guide

Deploying QuizAPI on DigitalOcean: A Production Setup Guide

Deploy a production-ready quiz API on DigitalOcean with PM2 process management, Nginx reverse proxy, SSL, and PostgreSQL.

Bobby Iliev2026-04-088 min read
Share:

From Local to Production

Your quiz API works on your machine. Now you need it to work for everyone, reliably, with HTTPS, behind a reverse proxy, and with a managed database that handles backups. DigitalOcean is a solid choice for this - the pricing is predictable, the setup is straightforward, and you get managed PostgreSQL without running your own database server.

This guide walks you through the full production deployment: Droplet setup, Node.js with PM2, Nginx reverse proxy, Let's Encrypt SSL, and managed PostgreSQL. By the end, your API will be live at https://api.yourdomain.com.

If you do not have a DigitalOcean account yet, you can sign up here and get free credits to follow along.

Prerequisites

  • A domain name with DNS access
  • An SSH key pair for server access
  • Your quiz API code in a Git repository

Creating the Droplet

Log in to DigitalOcean and create a new Droplet:

  • Image: Ubuntu 24.04 LTS
  • Plan: Basic, 2 vCPUs / 4 GB RAM ($24/month) - sufficient for most quiz APIs
  • Datacenter: Choose the region closest to your users
  • Authentication: SSH key (never use password auth for production)

Once the Droplet is created, point your domain's DNS A record to the Droplet's IP address:

api.yourdomain.com -> YOUR_DROPLET_IP

Initial Server Setup

SSH into the Droplet and secure it:

ssh root@YOUR_DROPLET_IP

Create a non-root user:

adduser deploy usermod -aG sudo deploy # Copy SSH keys to the new user rsync --archive --chown=deploy:deploy ~/.ssh /home/deploy

Set up the firewall:

ufw allow OpenSSH ufw allow 'Nginx Full' ufw enable

Switch to the deploy user for all subsequent steps:

su - deploy

Installing Node.js

Install Node.js 20 LTS using the NodeSource repository:

curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash - sudo apt-get install -y nodejs

Verify the installation:

node --version npm --version

Setting Up the Application

Clone your repository and install dependencies:

mkdir -p ~/apps cd ~/apps git clone [email protected]:yourusername/quiz-api.git cd quiz-api npm ci --production

Create the environment file:

nano ~/.env.quiz-api

Add your production configuration:

NODE_ENV=production PORT=3000 DATABASE_URL=postgresql://user:password@db-host:25060/quizapi?sslmode=require QUIZAPI_SECRET=your-secret-key API_KEY_SALT=your-random-salt

Secure the file:

chmod 600 ~/.env.quiz-api

Run the database migrations:

cd ~/apps/quiz-api export $(cat ~/.env.quiz-api | xargs) npx prisma migrate deploy

PM2 Process Management

PM2 keeps your application running, restarts it on crashes, and manages logs:

sudo npm install -g pm2

Create a PM2 ecosystem file at ~/apps/quiz-api/ecosystem.config.cjs:

1module.exports = { 2 apps: [ 3 { 4 name: "quiz-api", 5 script: "dist/server.js", 6 instances: "max", 7 exec_mode: "cluster", 8 env_file: "/home/deploy/.env.quiz-api", 9 max_memory_restart: "500M", 10 log_date_format: "YYYY-MM-DD HH:mm:ss Z", 11 error_file: "/home/deploy/logs/quiz-api-error.log", 12 out_file: "/home/deploy/logs/quiz-api-out.log", 13 merge_logs: true, 14 }, 15 ], 16};

Create the logs directory and start the application:

mkdir -p ~/logs cd ~/apps/quiz-api npm run build pm2 start ecosystem.config.cjs

Verify it is running:

pm2 status pm2 logs quiz-api --lines 20

Set PM2 to start on boot:

pm2 startup systemd # Run the command it outputs pm2 save

Nginx Reverse Proxy

Install Nginx:

sudo apt install -y nginx

Create the site configuration:

sudo nano /etc/nginx/sites-available/quiz-api
1upstream quiz_api { 2 server 127.0.0.1:3000; 3 keepalive 64; 4} 5 6server { 7 listen 80; 8 server_name api.yourdomain.com; 9 10 # Security headers 11 add_header X-Frame-Options "SAMEORIGIN" always; 12 add_header X-Content-Type-Options "nosniff" always; 13 add_header X-XSS-Protection "1; mode=block" always; 14 add_header Referrer-Policy "strict-origin-when-cross-origin" always; 15 16 # Request size limit 17 client_max_body_size 10M; 18 19 # Gzip compression 20 gzip on; 21 gzip_types application/json text/plain application/javascript; 22 gzip_min_length 1000; 23 24 location / { 25 proxy_pass http://quiz_api; 26 proxy_http_version 1.1; 27 proxy_set_header Upgrade $http_upgrade; 28 proxy_set_header Connection 'upgrade'; 29 proxy_set_header Host $host; 30 proxy_set_header X-Real-IP $remote_addr; 31 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 32 proxy_set_header X-Forwarded-Proto $scheme; 33 proxy_cache_bypass $http_upgrade; 34 proxy_read_timeout 60s; 35 proxy_send_timeout 60s; 36 } 37 38 # Health check endpoint - no logging 39 location /health { 40 proxy_pass http://quiz_api; 41 access_log off; 42 } 43}

Enable the site and reload Nginx:

sudo ln -s /etc/nginx/sites-available/quiz-api /etc/nginx/sites-enabled/ sudo nginx -t sudo systemctl reload nginx

Test that the proxy works:

curl http://api.yourdomain.com/health

SSL with Let's Encrypt

Install Certbot:

sudo apt install -y certbot python3-certbot-nginx

Obtain and configure the SSL certificate:

sudo certbot --nginx -d api.yourdomain.com

Certbot automatically modifies your Nginx config to redirect HTTP to HTTPS and adds the certificate paths. Verify auto-renewal works:

sudo certbot renew --dry-run

Managed PostgreSQL

Create a managed PostgreSQL database from the DigitalOcean control panel:

  • Engine: PostgreSQL 16
  • Plan: Basic, 1 vCPU / 1 GB RAM ($15/month)
  • Datacenter: Same region as your Droplet

After creation, restrict access to your Droplet's IP in the Trusted Sources settings. Copy the connection string and update your environment file:

nano ~/.env.quiz-api # Update DATABASE_URL with the connection string from DigitalOcean

Restart the application:

cd ~/apps/quiz-api export $(cat ~/.env.quiz-api | xargs) npx prisma migrate deploy pm2 restart quiz-api

Deployment Script

Automate future deployments with a script at ~/deploy.sh:

1#!/bin/bash 2set -e 3 4APP_DIR=~/apps/quiz-api 5LOG_FILE=~/logs/deploy-$(date +%Y%m%d-%H%M%S).log 6 7echo "Starting deployment at $(date)" | tee "$LOG_FILE" 8 9cd "$APP_DIR" 10 11# Pull latest code 12echo "Pulling latest code..." | tee -a "$LOG_FILE" 13git pull origin main 2>&1 | tee -a "$LOG_FILE" 14 15# Install dependencies 16echo "Installing dependencies..." | tee -a "$LOG_FILE" 17npm ci --production 2>&1 | tee -a "$LOG_FILE" 18 19# Build 20echo "Building..." | tee -a "$LOG_FILE" 21npm run build 2>&1 | tee -a "$LOG_FILE" 22 23# Run migrations 24echo "Running database migrations..." | tee -a "$LOG_FILE" 25export $(cat ~/.env.quiz-api | xargs) 26npx prisma migrate deploy 2>&1 | tee -a "$LOG_FILE" 27 28# Reload PM2 with zero-downtime 29echo "Reloading application..." | tee -a "$LOG_FILE" 30pm2 reload quiz-api 2>&1 | tee -a "$LOG_FILE" 31 32echo "Deployment complete at $(date)" | tee -a "$LOG_FILE" 33 34# Verify health 35sleep 2 36HTTP_STATUS=$(curl -s -o /dev/null -w "%{http_code}" https://api.yourdomain.com/health) 37if [ "$HTTP_STATUS" = "200" ]; then 38 echo "Health check passed" | tee -a "$LOG_FILE" 39else 40 echo "WARNING: Health check returned $HTTP_STATUS" | tee -a "$LOG_FILE" 41fi

Make it executable:

chmod +x ~/deploy.sh

Monitoring

Set up basic monitoring with PM2 and system tools:

1# Check application status 2pm2 monit 3 4# View logs in real-time 5pm2 logs quiz-api 6 7# Check system resources 8htop 9 10# Check disk usage 11df -h

For more comprehensive monitoring, see our guide on Monitoring Quiz API Performance with Prometheus and Grafana.

Summary

Your quiz API is now running in production with:

  • PM2 handling process management, clustering, and automatic restarts
  • Nginx as a reverse proxy with security headers and gzip compression
  • Let's Encrypt providing free, auto-renewing SSL certificates
  • Managed PostgreSQL with automated backups and easy scaling
  • A deployment script for zero-downtime updates

The total cost starts at around $39/month on DigitalOcean - $24 for the Droplet and $15 for managed PostgreSQL. Scale up as your traffic grows by resizing the Droplet or adding read replicas to the database.

Test Your Knowledge

Think you understand Guide? Put your skills to the test with hands-on quiz questions.

Guide
Start Practicing

Enjoyed this article?

Share it with your team or try our quiz platform.

Stay Updated

Get the latest tutorials and API tips delivered to your inbox.

No spam, unsubscribe anytime.