Skip to content

Run Node.js as a persistent VPS service ​

This example uses systemd on Ubuntu or Debian. It assumes a reviewed Node.js application and a supported Node.js installation already exist. Adjust paths and the entry point to your project.

Steps ​

  1. Create a dedicated service user and private release directory. Deploy the reviewed project there, owned by that user. Run dependency installation and the build as that user, not as root.

    bash
    sudo adduser --system --group --home /srv/apnapp apnapp
    sudo install -d -o apnapp -g apnapp /srv/apnapp/current
  2. Install locked dependencies, build the production release and configure the application to listen on 127.0.0.1:3000. Environment settings only work if the app reads them. Keep secrets in a restricted environment file, not the source repository.

    bash
    sudo test -e /etc/apnapp.env || sudo install -o root -g apnapp -m 640 /dev/null /etc/apnapp.env
    command -v node
  3. Create /etc/systemd/system/apnapp.service with the configuration below. Adjust the Node executable path, working directory and entry file. Fill the environment file privately; do not overwrite an existing file containing secrets.

    ini
    [Unit]
    Description=APNCloud application
    After=network.target
    
    [Service]
    User=apnapp
    Group=apnapp
    WorkingDirectory=/srv/apnapp/current
    Environment=NODE_ENV=production
    EnvironmentFile=/etc/apnapp.env
    ExecStart=/usr/bin/node /srv/apnapp/current/server.js
    Restart=on-failure
    RestartSec=5
    NoNewPrivileges=true
    PrivateTmp=true
    
    [Install]
    WantedBy=multi-user.target
  4. Validate the unit, start it and inspect the logs. Fix startup errors before publishing any route.

    bash
    sudo systemd-analyze verify /etc/systemd/system/apnapp.service
    sudo systemctl daemon-reload
    sudo systemctl enable --now apnapp
    sudo journalctl -u apnapp -n 50 --no-pager
    curl -I http://127.0.0.1:3000/
  5. After a release, restart the service and check health. Plan a reboot test and keep the previous release for rollback. A restart can briefly interrupt requests.

If something goes wrong ​

If it works in a shell but fails as a service, compare the executable path, permissions, working directory and environment. Do not run the service as root to bypass a permission problem. Use application-specific writable directories for uploads and persistent data.

Last reviewed ​

2026-10-28