Tutorial · VPS

How to Deploy a Node.js App or Bot on DigitalOcean

From sign-up to a working site on https in about an hour: we create a server, secure it, run the app, connect a domain and SSL. Every command is ready to copy.

· 6 min read

Some links on this page are affiliate links: if you buy through them, we earn a commission at no extra cost to you. We only recommend what we use ourselves. Learn more

DigitalOcean is the cloud provider where we most often deploy Telegram bots, CRMs and Node.js sites. It wins on reliability, a clear control panel, servers ready in a minute and some of the best documentation in the industry. Below is our everyday deployment workflow, which works for Express or Next.js as well as for a Telegraf or grammY bot.

This guide is for Ubuntu 24.04 LTS. You will need a domain and basic familiarity with the terminal. If you would rather not deal with it, get in touch and we will set everything up for you.

Step 1. Sign up and get starting credit

Sign up for DigitalOcean and add a card. New users who sign up through a referral link get credit for the first 60 days, which is more than enough to test everything at your own pace.

Step 2. SSH key

Logging in with a key is more secure than a password. If you don't have a key yet, create one on your own computer:

ssh-keygen -t ed25519 -C "you@example.com"
cat ~/.ssh/id_ed25519.pub

Copy the public key it prints: you will need it when creating the server.

Step 3. Create a Droplet

In the control panel, click Create → Droplets and choose:

  • Region: the one closest to your audience; for Europe that's Frankfurt or Amsterdam.
  • Image: Ubuntu 24.04 (LTS) x64.
  • Size: Basic, Regular, 1 GB of RAM. That is enough for a bot or a small site. You can upgrade later.
  • Authentication: SSH Key, paste the key from step 2.
  • Backups: turn them on if the server will hold data you would hate to lose (it costs a fraction of the server price).

In a minute the server is ready and its IP address appears in the panel.

Step 4. First login and a separate user

ssh root@SERVER_IP
apt update && apt upgrade -y
adduser deploy
usermod -aG sudo deploy
rsync --archive --chown=deploy:deploy ~/.ssh /home/deploy

From here on we work as deploy, not root: that way a bug in the app can't give access to the whole system.

Step 5. Firewall

We open only SSH and the web ports:

ufw allow OpenSSH
ufw allow 80,443/tcp
ufw enable

Alternative: the Cloud Firewall in the DigitalOcean panel, which filters traffic before it reaches the server and is configured with a few clicks.

Step 6. Install Node.js

We install the current LTS version from the official NodeSource repository (24.x at the time of writing):

ssh deploy@SERVER_IP
curl -fsSL https://deb.nodesource.com/setup_24.x | sudo -E bash -
sudo apt install -y nodejs git
node -v

Step 7. Get the code onto the server

git clone https://github.com/you/your-app.git ~/app
cd ~/app
npm ci --omit=dev

Keep secrets (the bot token, database passwords) in a .env file on the server, not in the repository.

Step 8. Autostart with systemd

systemd already ships with Ubuntu: it will start the app after a reboot and restart it if it crashes. Create the file /etc/systemd/system/app.service:

[Unit]
Description=Node.js app
After=network.target

[Service]
User=deploy
WorkingDirectory=/home/deploy/app
EnvironmentFile=/home/deploy/app/.env
ExecStart=/usr/bin/node server.js
Restart=always
RestartSec=3

[Install]
WantedBy=multi-user.target

Start it and watch the logs:

sudo systemctl daemon-reload
sudo systemctl enable --now app
journalctl -u app -f

For a Telegram bot using long polling, this is already enough: it's up and running. For a website or a webhook-based bot, keep going.

Step 9. nginx in front of the app

nginx accepts requests from the internet, passes them to the app on port 3000, and also handles HTTPS and compression.

sudo apt install -y nginx
sudo nano /etc/nginx/sites-available/app
server {
    listen 80;
    server_name example.com www.example.com;

    location / {
        proxy_pass http://127.0.0.1:3000;
        proxy_http_version 1.1;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
    }
}
sudo ln -s /etc/nginx/sites-available/app /etc/nginx/sites-enabled/
sudo rm /etc/nginx/sites-enabled/default
sudo nginx -t && sudo systemctl reload nginx

Step 10. Domain and free SSL

At your domain registrar, create an A record for example.com and www pointing to the server's IP address. Once DNS has updated (usually anywhere from a few minutes to an hour), get a Let's Encrypt certificate:

sudo apt install -y certbot python3-certbot-nginx
sudo certbot --nginx -d example.com -d www.example.com

Certbot adds HTTPS to the nginx config itself and sets up automatic certificate renewal.

Step 11. Backups and updates

  • Droplet backups in the panel take a copy of the whole server weekly or daily. You can also take a snapshot before risky changes.
  • Database: a separate daily dump to storage (for example, DigitalOcean Spaces) is a good idea even if you have server backups.
  • Security updates in Ubuntu are installed automatically via unattended-upgrades. Update everything else once a month: sudo apt update && sudo apt upgrade.

How much it costs

The most basic Droplet with 1 GB of RAM costs around $6 per month, and backups add 20-30% to the server price. You pay for the time the server actually runs: delete the server and billing stops. Prices are approximate as of October 2026; check the DigitalOcean website for current ones.

If you need lots of RAM for scrapers or several projects, and consistent performance is not critical, compare with Contabo: you get more resources for the same money.

Can I use pm2 instead of systemd?

Yes, pm2 is also a popular option with handy monitoring. We usually go with systemd because it is already part of the system and needs no extra packages.

What if my app runs in Docker?

Then instead of steps 6-8, install Docker and run the app with docker compose up -d and a restart policy. nginx and certbot are set up the same way.

What is App Platform, and isn't it simpler?

App Platform is DigitalOcean's PaaS: you connect GitHub and the platform builds and runs the app for you. It is simpler, but more expensive than a Droplet and less flexible. For a single small site it is a good option.

Is 1 GB of RAM enough?

For a bot or a small Node.js site, yes. If the same server will also run a database or a Next.js build, take 2 GB or add swap.

Message us