Skip to content

Instantly share code, notes, and snippets.

@Oluwatunmise-olat
Last active July 7, 2025 18:44
Show Gist options
  • Save Oluwatunmise-olat/b23641b48b9aec9bf2b9f14ed71ed265 to your computer and use it in GitHub Desktop.
Save Oluwatunmise-olat/b23641b48b9aec9bf2b9f14ed71ed265 to your computer and use it in GitHub Desktop.
Production VPS Checklist

Production VPS Deployment Guide

Initial Server Setup

  1. Update and upgrade the system:

    sudo dnf update -y
    sudo dnf upgrade -y
  2. Install essential packages:

    sudo dnf install epel-release net-tools curl wget vim git -y

    Note: Alternatively, you can use cloud-init for this step.

  3. Install and configure firewalld:

    sudo dnf install firewalld -y
    sudo systemctl start firewalld
    sudo systemctl enable firewalld
  4. (Optional) Install snapd:

    sudo dnf install snapd -y

    For more information, visit: https://snapcraft.io/docs/installing-snap-on-centos

User Management

  1. Create a new user:

    sudo adduser '<username>'
  2. Add user to the wheel group (for sudo access):

    sudo usermod -aG wheel '<username>'
  3. Set up password-less sudo access for the user:

    sudo visudo /etc/sudoers.d/<filename>

    Add this line to the file:

    <username>    ALL=(ALL)    NOPASSWD: ALL
    

    Validate the sudoers file:

    sudo visudo -cf /etc/sudoers
  4. Generate a password hash (if needed):

    mkpasswd -m sha-512

    or

    echo -n 'your_password' | openssl dgst -sha512
  5. Switch to the new user:

    su <username>
    cd ~

SSH Configuration

  1. Set up SSH directory and authorized_keys file:

    mkdir ~/.ssh
    chmod 700 ~/.ssh/
    touch ~/.ssh/authorized_keys
    chmod 600 ~/.ssh/authorized_keys
  2. Disable password authentication:

    sudo vim /etc/ssh/sshd_config

    Find and set PasswordAuthentication no

  3. Disable root login:

    sudo vim /etc/ssh/sshd_config

    Find and set PermitRootLogin no or PermitRootLogin prohibit-password

  4. Validate and reload SSH configuration:

    sudo sshd -t
    sudo systemctl reload sshd

Firewall Configuration

  1. Check firewall status:

    sudo systemctl status firewalld
  2. List allowed services:

    sudo firewall-cmd --list-services
  3. Open necessary ports (e.g., for HTTP and HTTPS):

    sudo firewall-cmd --add-service=http --zone=public --permanent
    sudo firewall-cmd --add-service=https --zone=public --permanent
  4. Open additional ports if needed (e.g., for a Node.js application on port 3000):

    sudo firewall-cmd --zone=public --add-port=3000/tcp --permanent
  5. Reload firewall to apply changes:

    sudo firewall-cmd --reload
  6. List open ports:

    sudo firewall-cmd --list-ports

Time Zone Configuration

  1. List available time zones:

    sudo timedatectl list-timezones
  2. Set the desired time zone (e.g., Africa/Lagos):

    sudo timedatectl set-timezone Africa/Lagos
  3. Verify the time zone setting:

    sudo timedatectl

Web Server Setup (Nginx)

  1. Install Nginx:

    sudo dnf install nginx -y
  2. Enable and start Nginx:

    sudo systemctl enable nginx
    sudo systemctl start nginx
  3. Configure Nginx for your application:

    cd /etc/nginx/conf.d
    sudo touch astroid-destroyer.conf
    sudo vim astroid-destroyer.conf

    Add your Nginx configuration (refer to https://blog.logrocket.com/how-to-run-node-js-server-nginx/ for details)

  4. Validate Nginx configuration:

    sudo nginx -t
  5. Reload Nginx to apply changes:

    sudo systemctl reload nginx
  6. Allow Nginx to make network connections (required for reverse proxy):

    sudo setsebool -P httpd_can_network_connect on
  7. Sample Nginx Config

    # /etc/nginx/conf.d/astroid-destroyer.conf
    server {
        # Listen on port 80 without the default_server directive
        listen 80;
    
        # Use wildcard to match any IP address or domain
        server_name _;  # This will catch all requests to the server
    
        # Proxy settings for Node.js app running on localhost:3002
        location / {
            proxy_pass http://localhost:3002;  # Node.js app running on port 3002
            proxy_http_version 1.1;
    
            # Forward real IP and other useful headers
            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_set_header Host $host;
            proxy_set_header X-NginX-Proxy true;
    
            # Handle WebSocket upgrades
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection 'upgrade';
            proxy_cache_bypass $http_upgrade;
        }
    
        # Gzip compression (optional)
        gzip on;
        gzip_types text/plain application/xml text/css application/javascript;
        gzip_min_length 1000;
    }

SSL Certificate Setup (Let's Encrypt with Certbot)

  1. Follow the installation guide at https://certbot.eff.org/instructions?ws=nginx&os=snap

  2. Obtain and install a certificate:

    sudo certbot --nginx -d astroid-destroyer.fun -n -m github@gmail.com --agree-tos
  3. Test automatic renewal:

    sudo certbot renew --dry-run
  4. Check the renewal timer:

    sudo systemctl list-timers

Node.js Setup

  1. Install NVM (Node Version Manager):

    curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.3/install.sh | bash
    source ~/.bashrc
  2. Install Node.js:

    nvm install --lts
    # Or install a specific version:
    nvm install <specific version>
    nvm use <version>
  3. (Optional) Set default Node.js version:

    nvm alias default <version>
  4. Install PM2 globally:

    npm install pm2 -g
  5. Set up PM2 as a systemd process:

    pm2 startup
    # Follow any prompted instructions
    sudo systemctl status pm2-<username>
    sudo systemctl enable pm2-<username>
    sudo systemctl start pm2-<username>

Application Deployment

  1. Create a PM2 ecosystem file (e.g., ecosystem.config.js):

    module.exports = {
      apps: [{
        name: 'match-fixture-1',
        script: './dist/src/index.js',
        env: {
          NODE_ENV: 'production',
          PORT: 3002,
        },
        env_production: {
          NODE_ENV: 'production',
        },
        exec_mode: 'cluster',
        instances: 2,
        autorestart: true,
        max_restarts: 5,
        exp_backoff_restart_delay: 100,
      }],
    }
  2. Start your application with PM2:

    pm2 start ecosystem.config.js --autorestart
  3. Check application status and scale if needed:

    pm2 describe match-fixture-1 | grep -i "port"
    pm2 scale match-fixture-1 2
    pm2 save

Configuring DNS on GoDaddy

To point a subdomain or domain to your server’s IP address using GoDaddy, follow these steps:

1. Log In to GoDaddy

  • Go to GoDaddy and log in to your account.
  • In your account dashboard, navigate to "My Products".
  • Locate your domain (e.g., wave-deploy.xyz) and click DNS to manage the DNS settings.

2. Add an A Record for Your Subdomain

  • In the DNS Management section, click on Add to create a new DNS record.
  • Fill out the following details:
    • Type: A
    • Name: api (This creates a subdomain, e.g., api.wave-deploy.xyz). If you're pointing the root domain, leave this field as @.
    • Value: Your server’s public IP address (e.g., 192.168.x.x).
    • TTL: Leave as default (600 seconds or 10 minutes is a good option).
  • Click Save to apply the changes.

3. Wait for DNS Propagation

DNS changes may take some time (usually within a few minutes but up to 24 hours) to propagate across the internet. To check the DNS propagation status, you can use a tool like DNSChecker.org.

  • Visit DNSChecker.org.
  • Enter your subdomain (api.wave-deploy.xyz) in the search bar.
  • Select A Record from the dropdown.
  • Click Search to view DNS propagation status across various countries.

Once the DNS record has propagated, your subdomain (api.wave-deploy.xyz) will point to your server’s IP, and any requests to the subdomain will be forwarded to the server.

4. Update Nginx (if applicable)

Ensure that your web server (e.g., Nginx) is configured to handle requests for the subdomain. You can update your Nginx configuration file to route requests for api.wave-deploy.xyz to the appropriate application on your server. Here’s an example:

server {
    listen 80;
    server_name api.wave-deploy.xyz;

    location / {
        proxy_pass http://localhost:3002;  # Adjust to your app's internal port
        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;
    }
}

Useful Commands

  • Check listening ports: netstat -tuln | grep <port>
  • Start SSH agent: eval $(ssh-agent)
  • Check file integrity: md5sum <file.gz>
  • View service logs: journalctl -xeu <service name>
  • Multi-line editing in Vim: Vim insert same characters across multiple lines

Additional Firewall Commands

# Get all firewall zones
sudo firewall-cmd --get-zones

# Get the default zone
sudo firewall-cmd --get-default-zone

# List all rules in the default zone
sudo firewall-cmd --list-all

# List all rules in all zones
sudo firewall-cmd --list-all-zones

# Get all services allowed in the default zone
sudo firewall-cmd --get-services

# Get all forward ports in the default zone
sudo firewall-cmd --list-forward-ports

# Get all ICMP blocks in the default zone
sudo firewall-cmd --list-icmp-blocks

# Get all rich rules in the default zone
sudo firewall-cmd --list-rich-rules

# Get direct rules (if any)
sudo firewall-cmd --direct --get-all-rules

# Check if a specific port is open (replace 80 with your port number)
sudo firewall-cmd --query-port=80/tcp

# List all active zones and the interfaces assigned to them
sudo firewall-cmd --get-active-zones

# Get the configuration for a specific zone (replace 'public' with zone name if needed)
sudo firewall-cmd --zone=public --list-all

# List all the permanent rules (rules that persist after reboot)
sudo firewall-cmd --permanent --list-all

Remember to replace <username>, <specific version>, and other placeholders with your actual values when using this guide.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment