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
- Get your VPS server on Hostinger (Use code
CODINGINFLOW
for 10% off). Install Ubuntu 24 as the OS and set a root password. - Log into your server as root:
ssh root@<your-server-ip>
- Update Linux packages:
apt update && apt upgrade -y
- Create a new user:
adduser <username>
- Add this user to the sudo group:
usermod -aG sudo <username>
- Logout from your server:
logout
- Log in with your new user account:
ssh <username>@<your-server-ip>
- Check if sudo works:
sudo -v
(If you don't get an error, it's good) - Create the SSH key folder:
mkdir ~/.ssh && chmod 700 ~/.ssh
- Confirm the folder exists:
ls -a
- Logout again:
logout
- Generate an SSH key on your local machine:
ssh-keygen -t ed25519 -C "[email protected]"
- 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>
- Log back into your server:
ssh <username>@<your-server-ip>
- Open SSH configuration:
sudo nano /etc/ssh/sshd_config
- Disable IPv6:
AddressFamily inet
- Disable password:
PasswordAuthentication no
- Disable root login:
PermitRootLogin no
- If there is an included
.conf
file, make it empty. E.g.sudo nano /etc/ssh/sshd_config.d/*.conf
- Restart SSH:
sudo systemctl restart ssh
(On some distros it'ssshd
instead ofssh
) - Logout, verify that root + password logins are disabled
- Log back into your server
- Install firewall:
sudo apt install ufw
- Whitelist ports:
sudo ufw allow ssh
+http
+https
- Enable firewall:
sudo ufw enable
- Check firewall status:
sudo ufw status
- 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
- Reboot the server:
sudo reboot
. Log back in once the server is reachable again. - 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
- Check Node + NPM version:
node -v
,npm -v
- Install Git & check version:
sudo apt install git
,git -v
- Install PostgreSQL:
sudo apt install postgresql postgresql-contrib
- Start PostgreSQL:
sudo systemctl start postgresql.service
- Check if PostgreSQL is running:
sudo service postgresql status
- Start the Postgres prompt:
sudo -u postgres psql
- 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>;
- Exit PostgreSQL:
\q
- Create an apps folder:
mkdir apps
- Navigate to /apps:
cd apps/
(Usecd ..
to go back) - Git clone the sample repo (or use your own):
git clone https://github.com/codinginflow/next-self-hosting
- Open the project folder:
cd next-self-hosting/
- 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"
- Install NPM packages:
npm install
- Install Nginx:
sudo apt install nginx
- 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;
}
}
- Link your new configuration file:
sudo ln -s /etc/nginx/sites-available/nextjs.conf /etc/nginx/sites-enabled/
- Remove the default configuration:
sudo rm /etc/nginx/sites-enabled/default
- Check if your Nginx config is valid:
sudo nginx -t
- Restart Nginx:
sudo service nginx restart
- Push the database schema (This depends on your ORM/DB library):
npx prisma db push
- Build and run the project:
npm run build
,npm start
- Now you can open your website at
http://<your-server-ip>
but the process will stop when you close the terminal. - Install PM2:
sudo npm install -g pm2
- Run your Next.js app via PM2:
pm2 start npm --name "nextjs" -- start
- Useful PM2 commands:
- Check status:
pm2 status
- Show logs:
pm2 logs
(Close withCtrl + C
) - Clear logs:
pm2 flush
- Restart Next.js app (to apply changes):
pm2 restart nextjs
- Show the PM2 startup script:
pm2 startup
. Copy and execute the printed command. - Now, even if you reboot your server, PM2 should restart your Next.js app automatically:
sudo reboot
- Buy a domain, for example at Namecheap
- Go to your Hostinger dashboard ->
DNS Manager
->Add Domain
and follow the instructions - 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;
- Restart Nginx:
sudo service nginx restart
- Install Certbot:
sudo snap install --classic certbot
- Prepare Certbot command:
sudo ln -s /snap/bin/certbot /usr/bin/certbot
- Install SSL certificates:
sudo certbot --nginx
- 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!