How I deploy NextJS with Systemd and Nginx

Sun May 19 2024


Some time ago I wrote the post How I deploy my NextJS Projects with Nginx and Cloudflare Tunnel. But since than I've changed the way I host my sites. I've removed Cloudflare Tunnel from my tech stack and decided to manage things by myself by switching to a PM2 + Nginx + Certbot based setup. But sometimes I just like to embrace the traditions like using Systemd instead of PM2.

So in this post I'm going to show you how I deploy my NextJS projects on Linux using nothing but Systemd and Nginx web server. Let's start!

Setting up user and Systemd

So first things first we need a different user without root access for running our website. And for that I'm gonna create a webd user (Stands for web-daemon) with /web as it's home folder.

sudo useradd -m -d /web webD

This command above creates our user and it's home directory /web for us. Now when you go to root folder you can see a new folder named web.

Now we need to change the owner group of that folder as our current user and give it right permissions. To do that we can simply use the commands below.

sudo chown -R webD:bt /web
sudo chmod -R ug+rwx /web

At that point you should be able to read, write and execute from that folder with no problem. You can verify that by running a simple touch command inside that folder.

Now we need to move our website source to this folder and create our Systemd service for it.

Before continuing I needed to verify that NodeJS has no issues with our permissions, so I moved an old version of this website and tested it out by running command below.

sudo -u webD npm run dev

And of course it worked perfectly fine. But in case of error, you can run the chmod command above and try again.

Now we need to create a file with the name of our project under /etc/systemd/system folder.

sudo vim /etc/systemd/system/web-project.service

And fill the file with the content like below. Don't forget to tune it for your project!

[Unit] 
Description=Projects Name
After=network.target

[Service] 
WorkingDirectory=/web/project 
User=webD
Environment="NODE_ENV=production" 
ExecStart=/usr/bin/npm run start

[Install] 
WantedBy=multi-user.target

At that point you should make sure that your npm binary is located at /usr/bin by using the command below otherwise it'll fail to run.

which npm

After that run the command below to enable our service.

sudo systemctl enable --now web-project

Now our NextJS website should be up and running in our local network. You can confirm that by using the command below and visiting the website from it's local address.

sudo systemctl status web-project

# Example output from tbnmc's website
● Caesium.service - TBNMC Website                                                
     Loaded: loaded (/etc/systemd/system/Caesium.service; enabled;
     Active: active (running) since Sun 2024-05-19 16:30:16 UTC; 3min 19s ago    
     Main PID: 3665 (npm run start)
     Tasks: 24 (limit: 2197)
     Memory: 164.3M               
     CPU: 3.361s
     CGroup: /system.slice/Caesium.service                                       
             ├─3665 "npm run start"                                              
             ├─3676 sh -c "next start -p 8090"        
             └─3677 next-router-worker                        
May 19 16:30:16 cork-server2 systemd[1]: Started TBNMC Website.                  May 19 16:30:17 cork-server2 npm[3665]: > Caesium@1.2.0 start                    May 19 16:30:17 cork-server2 npm[3665]: > next start -p 8090                     
May 19 16:30:17 cork-server2 npm[3677]:   ▲ Next.js 13.5.6                       May 19 16:30:17 cork-server2 npm[3677]:   - Local: http://localhost:8090  
May 19 16:30:17 cork-server2 npm[3677]:  ✓ Ready in 311ms

And this means our service is up and running perfectly fine! Now we can configure Nginx as reverse proxy and Certbot for TLS certificates.

Setting up Nginx and Certbot

Configuring Nginx as a reverse proxy and using Certbot with it is actually pretty easy. We just need to add a server config file that listens connections on port 80 and proxies that to our website which runs in background as a systemd service.

But before going any further we need to install those packages.

For dnf :

sudo dnf install nginx certbot python3-certbot-nginx

For apt :

sudo apt install nginx certbot python3-certbot-nginx

after that simply go to your Nginx config directory and create a new config file with your projects name.

(/etc/nginx/conf.d for RHEL based, /etc/nginx/sites-enabled for Debian based)

Now create file using command below and fill it's contents like the example below that command.

sudo vim web-project.conf
server {
    listen 80;
    server_name example.com;

    location / {
        add_header          Access-Control-Max-Age "3600" always;
        add_header          Strict-Transport-Security "max-age=31536000" always;
        add_header          X-Frame-Options deny;
        
        proxy_pass          http://localhost:3000;
        
        proxy_set_header    Host $host;
        proxy_set_header    X-Real-IP $remote_addr;
        proxy_set_header    X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}

You need to change the server_name with your domain and the port 3000 with the current port your server runs on in proxy_pass section.

After that restart the Nginx and after that (assuming that you've done with DNS settings) when you visit your website under your domain, everything should work.

Now only thing we need to do is getting our TLS certs for secure Https connections. For that we're gonna use Certbot that we installed earlier. Believe me it's easy as running a single command.

To get your tls certs for your website use the command below and follow it's instructions.

sudo certbot --nginx -d example.com

After that restart Nginx, you should be able to visit your website using Https.

sudo systemctl restart nginx

And we're done!

This is a way I use for servers that only hosts 1 or 2 websites, for more complex setups like load balancing, multi-threading and other stuff I still use PM2. But I believe it's an overkill for hosting a simple static blog site like I have with TbnMC so this is an alternative way to follow.

So that's it for this post. If you got any problems with your setup you can always send an e-mail or leave me a comment on Mastodon, I'll be happy to help you. As always, thanks for reading!

Reply via E-Mail

Thank You!

19.05.2024 - 51/100