Self-Hosting Guide

Deploy PulseGen on your own infrastructure.

Table of contents

  1. Deployment Options
  2. Docker Compose (Recommended)
    1. 1. Server Requirements
    2. 2. Install Docker
    3. 3. Clone and Configure
    4. 4. Configure Docker Compose
    5. 5. Configure Nginx
    6. 6. SSL Certificate
    7. 7. Deploy
  3. Security Checklist
    1. Before Going Live
    2. Database Security
    3. Firewall Configuration
  4. Updating
  5. Monitoring
    1. Health Checks
    2. Log Management
  6. Scaling
    1. Horizontal Scaling with Docker Swarm
    2. Performance Tuning
  7. Troubleshooting
    1. Container Won’t Start
    2. Database Connection Issues
    3. SSL Certificate Issues
  8. Support

Deploy PulseGen on your own infrastructure for complete data ownership and control.

Deployment Options

Method Best For Complexity
Docker Compose Small teams, single server Low
Docker Swarm Medium scale, HA required Medium
Kubernetes Large scale, enterprise High

1. Server Requirements

  • CPU: 2+ cores
  • RAM: 4GB minimum, 8GB recommended
  • Storage: 20GB+ SSD
  • OS: Ubuntu 22.04 LTS, Debian 12, or similar

2. Install Docker

1
2
3
4
5
6
7
8
# Install Docker
curl -fsSL https://get.docker.com | sh

# Add user to docker group
sudo usermod -aG docker $USER

# Install Docker Compose plugin
sudo apt install docker-compose-plugin

3. Clone and Configure

1
2
3
4
5
git clone https://github.com/your-org/pulsegen.git
cd pulsegen

# Create production environment file
cp backend/.env.example backend/.env

Edit backend/.env:

# Production settings
NODE_ENV=production
PORT=5001

# Database (use Docker service name)
DATABASE_URL=postgresql://pulsegen:secure_password@postgres:5432/pulsegen

# Security (generate unique values!)
JWT_SECRET=<generate-with-openssl-rand-hex-32>
JWT_REFRESH_SECRET=<generate-with-openssl-rand-hex-32>
ENCRYPTION_KEY=<generate-with-openssl-rand-hex-32>

# Your domain
APP_URL=https://surveys.yourdomain.com
CORS_ORIGIN=https://surveys.yourdomain.com

# Redis (optional but recommended)
REDIS_URL=redis://redis:6379

4. Configure Docker Compose

Create docker-compose.prod.yml:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
version: '3.8'

services:
  postgres:
    image: postgres:16-alpine
    restart: always
    environment:
      POSTGRES_DB: pulsegen
      POSTGRES_USER: pulsegen
      POSTGRES_PASSWORD: ${DB_PASSWORD}
    volumes:
      - postgres_data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U pulsegen"]
      interval: 10s
      timeout: 5s
      retries: 5

  redis:
    image: redis:7-alpine
    restart: always
    volumes:
      - redis_data:/data
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 10s
      timeout: 5s
      retries: 5

  backend:
    build: ./backend
    restart: always
    depends_on:
      postgres:
        condition: service_healthy
      redis:
        condition: service_healthy
    environment:
      - NODE_ENV=production
    env_file:
      - ./backend/.env
    expose:
      - "5001"

  frontend:
    build: ./frontend
    restart: always
    depends_on:
      - backend
    expose:
      - "80"

  nginx:
    image: nginx:alpine
    restart: always
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf:ro
      - ./ssl:/etc/nginx/ssl:ro
    depends_on:
      - frontend
      - backend

volumes:
  postgres_data:
  redis_data:

5. Configure Nginx

Create nginx.conf:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
events {
    worker_connections 1024;
}

http {
    upstream frontend {
        server frontend:80;
    }

    upstream backend {
        server backend:5001;
    }

    server {
        listen 80;
        server_name surveys.yourdomain.com;
        return 301 https://$server_name$request_uri;
    }

    server {
        listen 443 ssl http2;
        server_name surveys.yourdomain.com;

        ssl_certificate /etc/nginx/ssl/fullchain.pem;
        ssl_certificate_key /etc/nginx/ssl/privkey.pem;

        # API routes
        location /api {
            proxy_pass http://backend;
            proxy_http_version 1.1;
            proxy_set_header Host $host;
            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;
        }

        # Frontend
        location / {
            proxy_pass http://frontend;
            proxy_http_version 1.1;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
        }
    }
}

6. SSL Certificate

Using Let’s Encrypt (recommended):

1
2
3
4
5
6
7
8
9
10
11
# Install certbot
sudo apt install certbot

# Get certificate
sudo certbot certonly --standalone -d surveys.yourdomain.com

# Copy certificates
mkdir -p ssl
sudo cp /etc/letsencrypt/live/surveys.yourdomain.com/fullchain.pem ssl/
sudo cp /etc/letsencrypt/live/surveys.yourdomain.com/privkey.pem ssl/
sudo chown -R $USER:$USER ssl/

7. Deploy

1
2
3
4
5
6
7
8
# Build and start
docker compose -f docker-compose.prod.yml up -d --build

# Check status
docker compose -f docker-compose.prod.yml ps

# View logs
docker compose -f docker-compose.prod.yml logs -f

Security Checklist

Before Going Live

  • Change all default passwords
  • Generate unique JWT secrets (32+ chars)
  • Generate unique encryption key (64 hex chars)
  • Enable HTTPS with valid SSL certificate
  • Configure firewall (allow only 80, 443, SSH)
  • Set up automated backups
  • Configure CORS to your domain only
  • Review rate limiting settings

Database Security

1
2
3
4
5
# Create backup
docker compose exec postgres pg_dump -U pulsegen pulsegen > backup.sql

# Schedule daily backups (add to crontab)
0 2 * * * cd /path/to/pulsegen && docker compose exec -T postgres pg_dump -U pulsegen pulsegen > /backups/pulsegen-$(date +\%Y\%m\%d).sql

Firewall Configuration

1
2
3
4
5
6
7
# UFW example
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow ssh
sudo ufw allow http
sudo ufw allow https
sudo ufw enable

Updating

1
2
3
4
5
6
7
8
# Pull latest changes
git pull origin main

# Rebuild and restart
docker compose -f docker-compose.prod.yml up -d --build

# Run migrations if needed
docker compose exec backend npx prisma migrate deploy

Monitoring

Health Checks

1
2
3
4
5
6
7
8
# Check all services
docker compose -f docker-compose.prod.yml ps

# Check backend health
curl https://surveys.yourdomain.com/api/health

# Check database connection
docker compose exec postgres pg_isready

Log Management

1
2
3
4
5
6
7
8
# View all logs
docker compose -f docker-compose.prod.yml logs

# Follow specific service
docker compose -f docker-compose.prod.yml logs -f backend

# Last 100 lines
docker compose -f docker-compose.prod.yml logs --tail=100 backend

Scaling

Horizontal Scaling with Docker Swarm

1
2
3
4
5
6
7
8
# Initialize swarm
docker swarm init

# Deploy as stack
docker stack deploy -c docker-compose.prod.yml pulsegen

# Scale backend
docker service scale pulsegen_backend=3

Performance Tuning

PostgreSQL (postgresql.conf):

1
2
3
shared_buffers = 256MB
effective_cache_size = 768MB
maintenance_work_mem = 128MB

Redis:

1
2
# Enable persistence
docker compose exec redis redis-cli CONFIG SET appendonly yes

Troubleshooting

Container Won’t Start

1
2
3
4
5
6
7
# Check logs
docker compose -f docker-compose.prod.yml logs backend

# Rebuild from scratch
docker compose -f docker-compose.prod.yml down
docker compose -f docker-compose.prod.yml build --no-cache
docker compose -f docker-compose.prod.yml up -d

Database Connection Issues

1
2
3
4
5
# Check database is running
docker compose exec postgres pg_isready -U pulsegen

# Check connection from backend
docker compose exec backend npm run db:check

SSL Certificate Issues

1
2
3
4
5
# Renew certificate
sudo certbot renew

# Reload nginx
docker compose exec nginx nginx -s reload

Support