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
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"]
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
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],
}),
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
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
3. π¦ Application Deployment
Clone the project
# Clone from GitLab
git clone https://gitlab.com/your-username/your-repo.git
cd your-repo
Environment configuration
# Create .env file for production
cp .env.production .env
# Or create manually
nano .env
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...
Build and start services
# Build and start
docker-compose up -d --build
# Verify everything works
docker-compose ps
docker-compose logs -f
API Verification
# Test API
curl http://localhost:3000
# Check Swagger
curl http://localhost:3000/docs
4. π Subdomain Configuration
DNS Configuration in Cloudflare
- Log in to your Cloudflare dashboard
- Go to DNS β Records
- Click Add record
- Configure:
-
Type:
A
-
Name:
api
-
IPv4 address:
YOUR_LIGHTSAIL_IP
- Proxy status: π Proxied
- TTL: Auto
-
Type:
Nginx Installation and Configuration
# Install Nginx
sudo apt install nginx -y
sudo systemctl start nginx
sudo systemctl enable nginx
Reverse proxy configuration
# Create configuration
sudo nano /etc/nginx/sites-available/api.yourdomain.com
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;
}
}
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
Firewall configuration
# Allow HTTP and HTTPS
sudo ufw allow 'Nginx Full'
# Check rules
sudo ufw status
5. π SSL Configuration with Cloudflare
In Cloudflare dashboard
- SSL/TLS β Overview β Mode: Flexible
- SSL/TLS β Edge Certificates β Always Use HTTPS: ON
- 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
6. π Automated Deployment Script
Create a script to facilitate future updates:
# Create script
nano deploy.sh
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"
# Make executable
chmod +x deploy.sh
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
Service management
# Restart a service
docker-compose restart nestjs_app
# Rebuild after changes
docker-compose up -d --build
# Stop all services
docker-compose down
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
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
Log rotation setup
# Configure log rotation for Docker
sudo nano /etc/docker/daemon.json
Add:
{
"log-driver": "json-file",
"log-opts": {
"max-size": "10m",
"max-file": "3"
}
}
# Restart Docker daemon
sudo systemctl restart docker
9. π‘οΈ Security Best Practices
Environment variables security
# Secure .env file permissions
chmod 600 .env
# Never commit .env files to version control
echo ".env*" >> .gitignore
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
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
π 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
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
SSL certificate issues
# Check Cloudflare SSL settings
# Verify DNS propagation
dig api.yourdomain.com
# Test SSL
openssl s_client -connect api.yourdomain.com:443
π Future Deployments
For updates:
- Push code to GitLab
- On server:
./deploy.sh
- 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;"
Backup automation
# Create automated backup script
nano backup.sh
#!/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"
Your production infrastructure is now ready! π
Top comments (0)