Help Running your server Docker and apps
Run a Node.js app or Discord bot
Install a current Node.js, copy your app up, and run it under systemd so it starts at boot and comes back after a crash.
This puts anything you'd start with node index.js on your server, and keeps it running through crashes and reboots with systemd. The example is a Discord bot in a folder called bot.
Install Node.js#
Your system's Node.js is usually behind. Install the current long-term version from NodeSource:
Ubuntu / Debian
curl -fsSL https://deb.nodesource.com/setup_24.x | sudo bash -sudo apt install -y nodejsAlmaLinux / Rocky
curl -fsSL https://rpm.nodesource.com/setup_24.x | sudo bash -sudo dnf install -y nodejsCheck it, and note where node lives, which the service file needs:
node --version && command -v nodeCopy your app up#
Make a user for the bot, so it doesn't run as root, and a folder for it:
sudo useradd --system --home /opt/bot --shell /usr/sbin/nologin botsudo mkdir -p /opt/bot && sudo chown alex:alex /opt/botFrom your own computer, send the files, leaving out node_modules and .env:
rsync -avz --exclude node_modules --exclude .env ./bot/ [email protected]:/opt/bot/Or clone it on the server: git clone https://git.example.com/alex/bot.git /opt/bot. Then, on the server, hand the folder to the bot's user and install its packages:
sudo chown -R bot:bot /opt/botcd /opt/bot && sudo -u bot npm ci --omit=devWith no package-lock.json, use npm install --omit=dev.
Keep the token secret#
Put tokens and passwords in a file only root can read:
sudo tee /etc/bot.env <<'EOF'
DISCORD_TOKEN=replace-me
NODE_ENV=production
EOFsudo chmod 600 /etc/bot.envThe app reads them as process.env.DISCORD_TOKEN, the same as from a .env file.
Run it as a service#
sudo tee /etc/systemd/system/bot.service <<'EOF'
[Unit]
Description=Discord bot
After=network-online.target
Wants=network-online.target
[Service]
User=bot
Group=bot
WorkingDirectory=/opt/bot
EnvironmentFile=/etc/bot.env
ExecStart=/usr/bin/node index.js
Restart=on-failure
RestartSec=5
NoNewPrivileges=true
[Install]
WantedBy=multi-user.target
EOFUse the path command -v node printed, and your app's start file, in ExecStart. Each line is explained in Run something on boot with systemd.
Start it, now and at every boot:
sudo systemctl daemon-reload && sudo systemctl enable --now botWatch its output to check it logged in:
sudo journalctl -u bot -fShip a new version#
Copy the new files up as before, then, on the server:
sudo chown -R bot:bot /opt/bot && cd /opt/bot && sudo -u bot npm ci --omit=dev && sudo systemctl restart botFirewalls and ports#
A Discord bot only connects out, so it needs no ports opened. An app people visit should listen on 127.0.0.1 with Caddy in front: see Host a website with HTTPS using Caddy.
Python, and other languages#
Any language works the same way; only ExecStart changes. A Python bot in a virtual environment:
ExecStart=/opt/bot/venv/bin/python bot.pyStill stuck? Open a ticket and say which guide you followed.