DEV Community

Cover image for How To Deploy Any Server Using Systemctl (Node JS as Example)
Ilham Syahid S
Ilham Syahid S

Posted on

How To Deploy Any Server Using Systemctl (Node JS as Example)

Suppose you have VM in the cloud, if you don't have you can have it free here. You may wonder how to deploy your apps so that can be access online.

Here's how to deploy any server using systemctl with nodejs as example.

Common way to do it by using docker, but our approach right now is using systemctl.
Systemctl is utility for controlling the systemd system and service manager.
I always analogy with the process that run along with system in paralel.

Create Simple App

Let's start with create simple webservice using nodejs and express.

Install dependencies

  1. Connect via SSH
  2. Update package Linux

    sudo apt update
    
  3. Install Node.js

    sudo apt install nodejs
    

    Check with:

    $ node -v
    v8.10.0
    

    You may have different version

  4. Install NPM

    sudo apt install npm
    

Create Express App

Quick start create express app here with some modification.

  1. Install executable

    npm install -g express-generator@4
    
  2. Create app

    Create starting app

    express ~/foo && cd ~/foo
    
  3. Install dependencies

    npm install
    
  4. Start server

    Before we start, we have to change port server 3000 to 80.

    sudo pico bin/www
    

    Search for 3000 or go to line (CTRL + SHIFT + _) 15 and change into 80.

    Exit (CTRL + X).

    Run server

    sudo npm start
    

    IMPORTANT: make sure it on the port 80

Your application now is running, but after you close the SSH the application will vanish and you cant access it.

Now, we're moving to the next step.
Stop the app, if it still running.

Create Service

Systemctl consist of many units system.

Check units with sudo systemctl list-units, bunch of unit will come up.

Here's the example:

ilhamsyahids@instance-1:~$ systemctl list-units
UNIT                             LOAD   ACTIVE SUB       DESCRIPTION
...                              ...    ...    ...       ...
accounts-daemon.service          loaded active running   Accounts Service
apparmor.service                 loaded active exited    AppArmor initialization
apport.service                   loaded active exited    LSB: automatic crash report generation
atd.service                      loaded active running   Deferred execution scheduler
blk-availability.service         loaded active exited    Availability of block devices
chrony.service                   loaded active running   chrony, an NTP client/server
...                              ...    ...    ...       ...
Enter fullscreen mode Exit fullscreen mode

Create Unit

In order to use systemctl, you have to create the unit.

Suppose you are in the app directory (/home/$USER/foo)

Create file, let's called foo.service

touch foo.service
Enter fullscreen mode Exit fullscreen mode

Open file then write

[Unit]
Description=Foo application

[Service]
User=<USER>
WorkingDirectory=/home/<USER>/foo
ExecStart=/usr/bin/npm start
Restart=on-failure

[Install]
WantedBy=multi-user.target
Enter fullscreen mode Exit fullscreen mode

For example:

[Unit]
Description=Foo application

[Service]
User=ilhamsyahids
WorkingDirectory=/home/ilhamsyahids/foo
ExecStart=/usr/bin/npm start
Restart=on-failure

[Install]
WantedBy=multi-user.target
Enter fullscreen mode Exit fullscreen mode

Will quickly deep dive in the end of article

Save and close.

Run Unit

  1. Move unit into systemd folder

    cp foo.service /etc/systemd/system
    
  2. Reload daemon

    systemctl daemon-reload
    
  3. Start unit

    systemctl start foo.service
    
  4. Check status unit

    systemctl status foo.service
    

    You'll find something like this:

    ilhamsyahids@instance-1:~$ systemctl status foo.service
    ● foo.service - Foo application
    Loaded: loaded (/etc/systemd/system/foo.service; disabled; vendor preset: enabled)
    Active: active (running) since Fri 2021-12-03 11:28:45 UTC; 5h 18min ago
    Main PID: 5405 (npm)
        Tasks: 21 (limit: 1120)
    CGroup: /system.slice/foo.service
            ├─5405 npm
            ├─5451 sh -c node ./bin/www
            └─5452 node ./bin/www
    
    Dec 03 11:29:05 instance-1 npm[5405]: GET / 304 787.911 ms - -
    Dec 03 11:29:05 instance-1 npm[5405]: GET /stylesheets/style.css 304 1.683 ms - -
    

Make sure your VM is open HTTP and HTTPS

Now your app will serve even when you are exit the SSH connection, your app will remains.

Deep Dive

Found article that describe structure in the unit here.

[Unit]
Description=Foo application

[Service]
User=ilhamsyahids
WorkingDirectory=/home/ilhamsyahids/foo
ExecStart=/usr/bin/npm start
Restart=on-failure

[Install]
WantedBy=multi-user.target
Enter fullscreen mode Exit fullscreen mode

Unit contains three section:

  • [Unit]

    Information about the unit.

  • [Service]

    Information about "what will you do"

  • [Install]

    Information about where unit will served

Focus on "what will you do":

  • Property User optional but make as least privilege.
  • Instead of using cd to our app folder, simply using WorkingDirectory=<path-app-folder>
  • ExecStart the important property for execute the app. Do not reference with alias such as npm start but point into the binary exec /usr/bin/npm start

    Suppose you have any server you can change exec here. e.g. Ruby: /home/user/.rbenv/shims/ruby main.rb

  • Restart=on-failure restart the process when it crashed

  • Environment the env variable. e.g: Environment="ENV=production"

Next Step

  • Serve using reserve proxy NGINX or Caddy
  • Deploy many app with multiple port in single VM (port forwarding)

Reach Me

Any issue? Reach me at ilhamsyahids.com or me@ilhamsyahids.com

Top comments (0)