How To Deploy Any Server Using Systemctl (Node JS as Example)
Tags: node, javascript, systemctl, server
Published: 2021-12-05 10:41:31

Suppose you have VM in the cloud, if you don't have you can have it free [here](https://ilhamsyahids.medium.com/lifetime-free-google-server-858bc32db037). 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.

<!-- truncate -->

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

    ```bash
    sudo apt update
    ```

3. Install Node.js

    ```bash
    sudo apt install nodejs
    ```

    Check with:

    ```bash
    $ node -v
    v8.10.0
    ```

    > You may have different version

4. Install NPM

    ```bash
    sudo apt install npm
    ```

### Create Express App

Quick start create express app [here](https://github.com/expressjs/express#quick-start) with some modification.

1. Install executable

    ```bash
    npm install -g express-generator@4
    ```

2. Create app

    Create starting app 

    ```bash
    express ~/foo && cd ~/foo
    ```

3. Install dependencies

    ```bash
    npm install
    ```

4. Start server

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

    ```bash
    sudo pico bin/www
    ```

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

    Exit (`CTRL + X`).

    Run server

    ```bash
    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:

```bash
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
...                              ...    ...    ...       ...
```

### 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`

```bash
touch foo.service
```

Open file then write

```ini
[Unit]
Description=Foo application

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

[Install]
WantedBy=multi-user.target
```

For example:

```ini
[Unit]
Description=Foo application

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

[Install]
WantedBy=multi-user.target
```

> Will quickly deep dive in [the end of article](#deep-dive)

Save and close.

### Run Unit

1. Move unit into systemd folder

    ```bash
    cp foo.service /etc/systemd/system
    ```

2. Reload daemon

    ```bash
    systemctl daemon-reload
    ```

3. Start unit

    ```bash
    systemctl start foo.service
    ```

4. Check status unit

    ```bash
    systemctl status foo.service
    ```

    You'll find something like this:

    ```bash
    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](https://linuxconfig.org/how-to-create-systemd-service-unit-in-linux). 

```ini
[Unit]
Description=Foo application

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

[Install]
WantedBy=multi-user.target
```

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](https://ilhamsyahids.com/) or *me@ilhamsyahids.com*