Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save oyal/221607fcb2453d34767be0064ee34500 to your computer and use it in GitHub Desktop.
Save oyal/221607fcb2453d34767be0064ee34500 to your computer and use it in GitHub Desktop.
Deploy & secure a Next.js app + Postgres DB on a VPS

Self-Hosting Next.js Tutorial

Follow the instructions below to deploy a Next.js app with a local PostgreSQL database to a VPS and connect it to a custom domain with free SSL. Watch the accompanying tutorial on YouTube: https://www.youtube.com/watch?v=2T_Dx7YgBFw

Instructions & commands:

  1. Get your VPS server on Hostinger (Use code CODINGINFLOW for 10% off). Install Ubuntu 24 as the OS and set a root password.
  2. Log into your server as root: ssh root@<your-server-ip>
  3. Update Linux packages: apt update && apt upgrade -y
  4. Create a new user: adduser <username>
  5. Add this user to the sudo group: usermod -aG sudo <username>
  6. Logout from your server: logout
  7. Log in with your new user account: ssh <username>@<your-server-ip>
  8. Check if sudo works: sudo -v (If you don't get an error, it's good)
  9. Create the SSH key folder: mkdir ~/.ssh && chmod 700 ~/.ssh
  10. Confirm the folder exists: ls -a
  11. Logout again: logout
  12. Generate an SSH key on your local machine: ssh-keygen -t ed25519 -C "[email protected]"
  13. Push this SSH key to your server:
  • Windows: scp $env:USERPROFILE/.ssh/id_ed25519.pub <username>@<your-server-ip>:~/.ssh/authorized_keys
  • Mac: scp ~/.ssh/id_ed25519.pub <username>@<your-server-ip>:~/.ssh/authorized_keys
  • Linux: ssh-copy-id <username>@<your-server-ip>
  1. Log back into your server: ssh <username>@<your-server-ip>
  2. Open SSH configuration: sudo nano /etc/ssh/sshd_config
  • Disable IPv6: AddressFamily inet
  • Disable password: PasswordAuthentication no
  • Disable root login: PermitRootLogin no
  1. If there is an included .conf file, make it empty. E.g. sudo nano /etc/ssh/sshd_config.d/*.conf
  2. Restart SSH: sudo systemctl restart ssh (On some distros it's sshd instead of ssh)
  3. Logout, verify that root + password logins are disabled
  4. Log back into your server
  5. Install firewall: sudo apt install ufw
  6. Whitelist ports: sudo ufw allow ssh + http + https
  7. Enable firewall: sudo ufw enable
  8. Check firewall status: sudo ufw status
  9. Open firewall rules: sudo nano /etc/ufw/before.rules
  • Add this to the INPUT block: -A ufw-before-input -p icmp --icmp-type echo-request -j DROP
  1. Reboot the server: sudo reboot. Log back in once the server is reachable again.
  2. Download & install Node.js:
  • curl -fsSL https://deb.nodesource.com/setup_22.x -o nodesource_setup.sh
  • sudo -E bash nodesource_setup.sh
  • sudo apt-get install nodejs -y
  1. Check Node + NPM version: node -v, npm -v
  2. Install Git & check version: sudo apt install git, git -v
  3. Install PostgreSQL: sudo apt install postgresql postgresql-contrib
  4. Start PostgreSQL: sudo systemctl start postgresql.service
  5. Check if PostgreSQL is running: sudo service postgresql status
  6. Start the Postgres prompt: sudo -u postgres psql
  7. Run these commands inside PostgreSQL to create a database + user:
  • CREATE DATABASE <database_name>;
  • CREATE USER <username> WITH ENCRYPTED PASSWORD '<password>';
  • ALTER ROLE <username> SET client_encoding TO 'utf8';
  • ALTER ROLE <username> SET default_transaction_isolation TO 'read committed';
  • ALTER ROLE <username> SET timezone TO 'UTC';
  • GRANT ALL PRIVILEGES ON DATABASE <database_name> TO <username>;
  • \c <database_name> postgres
  • GRANT ALL PRIVILEGES ON SCHEMA public TO <username>;
  1. Exit PostgreSQL: \q
  2. Create an apps folder: mkdir apps
  3. Navigate to /apps: cd apps/ (Use cd .. to go back)
  4. Git clone the sample repo (or use your own): git clone https://github.com/codinginflow/next-self-hosting
  5. Open the project folder: cd next-self-hosting/
  6. Create a .env file: sudo nano .env and add the following:
NEXT_PUBLIC_APP_TITLE="Self-Hosting Next.js Tutorial"
DATABASE_URL="postgresql://<username>:<password>@localhost:5432/<database_name>?schema=public"
  1. Install NPM packages: npm install
  2. Install Nginx: sudo apt install nginx
  3. Create an Nginx configuration file: sudo nano /etc/nginx/sites-available/nextjs.conf and insert the following (Don't forget to insert your server IP):
server {
 listen 80;
 server_name <your-server-ip>;

 location / {
  proxy_pass http://localhost:3000;
  proxy_http_version 1.1;
  proxy_set_header Upgrade $http_upgrade;
  proxy_set_header Connection 'upgrade';
  proxy_set_header Host $host;
  proxy_cache_bypass $http_upgrade;
  
  # Disable buffering to allow streaming responses
  proxy_buffering off;
  proxy_set_header X-Accel-Buffering no;
 }
}
  1. Link your new configuration file: sudo ln -s /etc/nginx/sites-available/nextjs.conf /etc/nginx/sites-enabled/
  2. Remove the default configuration: sudo rm /etc/nginx/sites-enabled/default
  3. Check if your Nginx config is valid: sudo nginx -t
  4. Restart Nginx: sudo service nginx restart
  5. Push the database schema (This depends on your ORM/DB library): npx prisma db push
  6. Build and run the project: npm run build, npm start
  7. Now you can open your website at http://<your-server-ip> but the process will stop when you close the terminal.
  8. Install PM2: sudo npm install -g pm2
  9. Run your Next.js app via PM2: pm2 start npm --name "nextjs" -- start
  10. Useful PM2 commands:
  • Check status: pm2 status
  • Show logs: pm2 logs (Close with Ctrl + C)
  • Clear logs: pm2 flush
  • Restart Next.js app (to apply changes): pm2 restart nextjs
  1. Show the PM2 startup script: pm2 startup. Copy and execute the printed command.
  2. Now, even if you reboot your server, PM2 should restart your Next.js app automatically: sudo reboot
  3. Buy a domain, for example at Namecheap
  4. Go to your Hostinger dashboard -> DNS Manager -> Add Domain and follow the instructions
  5. Update the server name in your Nginx config: sudo nano /etc/nginx/sites-available/nextjs.conf:
  • server_name your-domain.com www.your-domain.com;
  1. Restart Nginx: sudo service nginx restart
  2. Install Certbot: sudo snap install --classic certbot
  3. Prepare Certbot command: sudo ln -s /snap/bin/certbot /usr/bin/certbot
  4. Install SSL certificates: sudo certbot --nginx
  5. Test automatic rewenal: sudo certbot renew --dry-run

After all the DNS changes are applied (this can take up to 48h), your Next.js website will be available under your domain name with an active SSL certificate. Subscribe to Coding in Flow for more Next.js and web dev tutorials!

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