DEV Community

Georges HELOUSSATO
Georges HELOUSSATO

Posted on

Complete Guide: Deploy NestJS API with PostgreSQL on AWS Lightsail

This comprehensive guide walks you through deploying a NestJS application with PostgreSQL database on AWS Lightsail, featuring a custom subdomain managed by Cloudflare.

🎯 Final Objective

  • NestJS API accessible via https://api.yourdomain.com
  • Containerized PostgreSQL database
  • SSL managed by Cloudflare
  • Swagger documentation accessible

πŸ“‹ Prerequisites

  • AWS Lightsail server (Ubuntu)
  • Domain configured with Cloudflare
  • NestJS project on GitLab
  • SSH access to your server

1. πŸ“ Project Preparation

Required file structure

Your project should contain these files at the root:

your-project/
β”œβ”€β”€ src/                    # NestJS code
β”œβ”€β”€ package.json
β”œβ”€β”€ Dockerfile              # Docker configuration
β”œβ”€β”€ docker-compose.yml      # Service orchestration
β”œβ”€β”€ .env.production         # Production environment variables
β”œβ”€β”€ .dockerignore
└── .gitignore
Enter fullscreen mode Exit fullscreen mode

Dockerfile

FROM node:20

WORKDIR /app

# Copy package.json and package-lock.json
COPY package*.json ./

# Install all dependencies
RUN npm ci

# Copy source code
COPY . .

# Build the application
RUN npm run build

# Remove dev dependencies
RUN npm prune --production

# Expose port
EXPOSE 3000

# Start command
CMD ["npm", "run", "start:prod"]
Enter fullscreen mode Exit fullscreen mode

docker-compose.yml

version: '3.8'
services:
  postgres:
    image: postgres:15
    container_name: postgres_db
    restart: always
    environment:
      POSTGRES_DB: ${DB_NAME}
      POSTGRES_USER: ${DB_USERNAME}
      POSTGRES_PASSWORD: ${DB_PASSWORD}
    volumes:
      - postgres_data:/var/lib/postgresql/data
    networks:
      - app_network
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U ${DB_USERNAME} -d ${DB_NAME}"]
      interval: 10s
      timeout: 5s
      retries: 5

  nestjs_app:
    build: .
    container_name: nestjs_container
    restart: always
    environment:
      NODE_ENV: ${NODE_ENV}
      DB_HOST: postgres
      DB_PORT: ${DB_PORT}
      DB_USERNAME: ${DB_USERNAME}
      DB_PASSWORD: ${DB_PASSWORD}
      DB_NAME: ${DB_NAME}
      PORT: ${PORT}
      JWT_SECRET: ${JWT_SECRET}
      SWAGGER_PASSWORD: ${SWAGGER_PASSWORD}
      # Add all your other environment variables here
    ports:
      - "127.0.0.1:3000:3000"
    depends_on:
      postgres:
        condition: service_healthy
    networks:
      - app_network

volumes:
  postgres_data:

networks:
  app_network:
    driver: bridge
Enter fullscreen mode Exit fullscreen mode

NestJS Configuration (app.module.ts)

Ensure your main module uses the correct environment variables:

TypeOrmModule.forRootAsync({
  imports: [ConfigModule],
  useFactory: (configService: ConfigService) => ({
    type: 'postgres',
    host: configService.get('DB_HOST', 'localhost'),
    port: configService.get('DB_PORT', 5432),
    username: configService.get('DB_USERNAME', 'postgres'),
    password: configService.get('DB_PASSWORD', 'db_password'),
    database: configService.get('DB_NAME', 'myapp'),
    entities: [__dirname + '/**/*.entity{.ts,.js}'],
    autoLoadEntities: true,
    synchronize: configService.get('NODE_ENV') !== 'production',
  }),
  inject: [ConfigService],
}),
Enter fullscreen mode Exit fullscreen mode

2. πŸ”§ Lightsail Server Configuration

Connection and updates

# Connect to server
ssh -i your-key.pem ubuntu@your-lightsail-ip

# Update system
sudo apt update && sudo apt upgrade -y
Enter fullscreen mode Exit fullscreen mode

Docker Installation

# Install Docker
curl -fsSL https://get.docker.com -o get-docker.sh
sudo sh get-docker.sh

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

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

# Install Git
sudo apt install git -y

# Restart session
exit
# Reconnect
Enter fullscreen mode Exit fullscreen mode

3. πŸ“¦ Application Deployment

Clone the project

# Clone from GitLab
git clone https://gitlab.com/your-username/your-repo.git
cd your-repo
Enter fullscreen mode Exit fullscreen mode

Environment configuration

# Create .env file for production
cp .env.production .env

# Or create manually
nano .env
Enter fullscreen mode Exit fullscreen mode

Example .env file:

# Database Configuration
DB_HOST=postgres
DB_PORT=5432
DB_USERNAME=app_admin
DB_PASSWORD=YourSecurePassword2024!
DB_NAME=myapp

# Application Configuration
PORT=3000
NODE_ENV=production

# JWT Secret
JWT_SECRET=your_super_long_and_secure_jwt_secret_for_production_123456789

# Swagger Configuration
SWAGGER_PASSWORD=admin_prod_secure

# Your other environment variables...
Enter fullscreen mode Exit fullscreen mode

Build and start services

# Build and start
docker-compose up -d --build

# Verify everything works
docker-compose ps
docker-compose logs -f
Enter fullscreen mode Exit fullscreen mode

API Verification

# Test API
curl http://localhost:3000

# Check Swagger
curl http://localhost:3000/docs
Enter fullscreen mode Exit fullscreen mode

4. 🌐 Subdomain Configuration

DNS Configuration in Cloudflare

  1. Log in to your Cloudflare dashboard
  2. Go to DNS β†’ Records
  3. Click Add record
  4. Configure:
    • Type: A
    • Name: api
    • IPv4 address: YOUR_LIGHTSAIL_IP
    • Proxy status: 🟠 Proxied
    • TTL: Auto

Nginx Installation and Configuration

# Install Nginx
sudo apt install nginx -y
sudo systemctl start nginx
sudo systemctl enable nginx
Enter fullscreen mode Exit fullscreen mode

Reverse proxy configuration

# Create configuration
sudo nano /etc/nginx/sites-available/api.yourdomain.com
Enter fullscreen mode Exit fullscreen mode

File content:

server {
    listen 80;
    server_name api.yourdomain.com;

    location / {
        proxy_pass http://127.0.0.1:3000;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        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;
        proxy_cache_bypass $http_upgrade;
        proxy_read_timeout 300s;
        proxy_connect_timeout 300s;
        proxy_send_timeout 300s;
    }

    location /docs {
        proxy_pass http://127.0.0.1:3000/docs;
        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;
    }
}
Enter fullscreen mode Exit fullscreen mode

Enable configuration

# Create symbolic link
sudo ln -s /etc/nginx/sites-available/api.yourdomain.com /etc/nginx/sites-enabled/

# Test configuration
sudo nginx -t

# Reload Nginx
sudo systemctl reload nginx
Enter fullscreen mode Exit fullscreen mode

Firewall configuration

# Allow HTTP and HTTPS
sudo ufw allow 'Nginx Full'

# Check rules
sudo ufw status
Enter fullscreen mode Exit fullscreen mode

5. πŸ”’ SSL Configuration with Cloudflare

In Cloudflare dashboard

  1. SSL/TLS β†’ Overview β†’ Mode: Flexible
  2. SSL/TLS β†’ Edge Certificates β†’ Always Use HTTPS: ON
  3. Verify your DNS record is in Proxied mode (🟠)

Final test

# Test HTTP
curl http://api.yourdomain.com

# Test HTTPS
curl https://api.yourdomain.com

# Test Swagger
curl https://api.yourdomain.com/docs
Enter fullscreen mode Exit fullscreen mode

6. πŸš€ Automated Deployment Script

Create a script to facilitate future updates:

# Create script
nano deploy.sh
Enter fullscreen mode Exit fullscreen mode

Content:

#!/bin/bash

echo "πŸš€ Deployment in progress..."

# Pull code from GitLab
git pull origin main

# Copy production environment file
cp .env.production .env

# Stop existing services
docker-compose down

# Rebuild and restart
docker-compose up -d --build

echo "βœ… Deployment completed!"
echo "🌐 API available at: https://api.yourdomain.com"
Enter fullscreen mode Exit fullscreen mode
# Make executable
chmod +x deploy.sh
Enter fullscreen mode Exit fullscreen mode

7. πŸ”§ Maintenance Commands

Log monitoring

# View all logs
docker-compose logs -f

# Specific logs
docker-compose logs -f nestjs_app
docker-compose logs -f postgres
Enter fullscreen mode Exit fullscreen mode

Service management

# Restart a service
docker-compose restart nestjs_app

# Rebuild after changes
docker-compose up -d --build

# Stop all services
docker-compose down
Enter fullscreen mode Exit fullscreen mode

Database backup

# Create backup
docker exec postgres_db pg_dump -U your_username your_db_name > backup.sql

# Restore backup
cat backup.sql | docker exec -i postgres_db psql -U your_username -d your_db_name
Enter fullscreen mode Exit fullscreen mode

8. πŸ“Š Health Monitoring

Basic monitoring commands

# Check container status
docker-compose ps

# Monitor resource usage
docker stats

# Check disk usage
df -h
docker system df
Enter fullscreen mode Exit fullscreen mode

Log rotation setup

# Configure log rotation for Docker
sudo nano /etc/docker/daemon.json
Enter fullscreen mode Exit fullscreen mode

Add:

{
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "10m",
    "max-file": "3"
  }
}
Enter fullscreen mode Exit fullscreen mode
# Restart Docker daemon
sudo systemctl restart docker
Enter fullscreen mode Exit fullscreen mode

9. πŸ›‘οΈ Security Best Practices

Environment variables security

# Secure .env file permissions
chmod 600 .env

# Never commit .env files to version control
echo ".env*" >> .gitignore
Enter fullscreen mode Exit fullscreen mode

Firewall configuration

# Enable UFW
sudo ufw enable

# Allow only necessary ports
sudo ufw allow ssh
sudo ufw allow 'Nginx Full'

# Check firewall status
sudo ufw status verbose
Enter fullscreen mode Exit fullscreen mode

Database security

# Change default PostgreSQL port (optional)
# In docker-compose.yml, change port mapping to:
# ports:
#   - "127.0.0.1:5433:5432"

# Regular security updates
sudo apt update && sudo apt upgrade -y
Enter fullscreen mode Exit fullscreen mode

πŸŽ‰ Final Result

Your NestJS API is now accessible via:

  • API: https://api.yourdomain.com
  • Documentation: https://api.yourdomain.com/docs
  • Automatic SSL via Cloudflare
  • Containerized PostgreSQL database with persistent storage

🚨 Troubleshooting

Common issues and solutions

API not responding

# Check container status
docker-compose ps

# Check logs
docker-compose logs nestjs_app

# Restart if needed
docker-compose restart nestjs_app
Enter fullscreen mode Exit fullscreen mode

Database connection issues

# Check PostgreSQL logs
docker-compose logs postgres

# Verify environment variables
docker-compose config

# Reset database (caution: data loss)
docker-compose down
docker volume rm your-project_postgres_data
docker-compose up -d
Enter fullscreen mode Exit fullscreen mode

SSL certificate issues

# Check Cloudflare SSL settings
# Verify DNS propagation
dig api.yourdomain.com

# Test SSL
openssl s_client -connect api.yourdomain.com:443
Enter fullscreen mode Exit fullscreen mode

πŸ”„ Future Deployments

For updates:

  1. Push code to GitLab
  2. On server: ./deploy.sh
  3. Verify everything works

πŸ“ Additional Notes

Performance optimization

# Monitor API performance
curl -w "@curl-format.txt" -o /dev/null -s "https://api.yourdomain.com"

# Database performance monitoring
docker exec -it postgres_db psql -U your_username -d your_db_name -c "SELECT * FROM pg_stat_activity;"
Enter fullscreen mode Exit fullscreen mode

Backup automation

# Create automated backup script
nano backup.sh
Enter fullscreen mode Exit fullscreen mode
#!/bin/bash
DATE=$(date +%Y%m%d_%H%M%S)
docker exec postgres_db pg_dump -U your_username your_db_name > "backup_${DATE}.sql"
echo "Backup created: backup_${DATE}.sql"
Enter fullscreen mode Exit fullscreen mode

Your production infrastructure is now ready! πŸš€

Top comments (0)