Deploying a Node.js App to DigitalOcean: A Practical Guide from Zero to Production
Kim BoenderSo you've built a Node.js app and it works great on localhost. Now what?
Getting from a working local app to a live, production-ready deployment is one of those skill gaps that trips up a lot of developers, juniors and seniors alike. In this guide, we'll walk through deploying a Node.js application to DigitalOcean step by step. We'll cover spinning up a Droplet (their virtual machine product), configuring a Linux server from scratch, setting up Nginx as a reverse proxy, and keeping your app alive with PM2.
By the end, you'll have a publicly accessible app running on a real server, and you'll understand why each step exists.
New to DigitalOcean? You can sign up and get $200 in free credit for 60 days, which is more than enough to follow this entire guide without spending a cent.
Why DigitalOcean?
There are plenty of cloud providers, but for many developers building side projects, SaaS products, or freelancing, DigitalOcean hits a sweet spot.
Pricing is predictable. No surprise bills. You know what you pay. The documentation and community tutorials are genuinely excellent. Droplets start at $4/month, which is hard to beat for a hobby project or MVP. And there's a strong developer community with tons of tutorials, Q&A, and guides.
For larger teams or AI workloads, DigitalOcean also offers Kubernetes, Managed Databases, and Gradient AI Inference Cloud, but that's a topic for another post.
What we're building
We'll deploy a simple Express.js REST API. The concepts apply to any Node.js app: Next.js, Fastify, NestJS, etc. Express just makes the example easy to follow.
Prerequisites: basic comfort with the terminal and SSH, a Node.js app ready to deploy, and a DigitalOcean account.
Step 1: Create a DigitalOcean Droplet
A Droplet is DigitalOcean's term for a virtual machine. Log into your dashboard and click Create > Droplets.
Choose a region closest to your target users. For the image, select Ubuntu 22.04 LTS: stable, widely supported, with excellent community resources. For the plan, the Basic / Regular tier at $6/month (1 vCPU, 1GB RAM, 25GB SSD) is plenty to start.
For authentication, choose SSH Key (strongly recommended over password). If you don't have an SSH key pair, generate one locally:
ssh-keygen -t ed25519 -C "your_email@example.com"Then paste the contents of ~/.ssh/id_ed25519.pub into the DigitalOcean SSH key field. Give your Droplet a hostname and click Create. Within about 60 seconds, your server is live.
Step 2: Connect to your server
ssh root@YOUR_DROPLET_IPYou should be greeted by the Ubuntu welcome message. You're now inside your virtual server.
Running everything as root is a security risk. Create a deploy user:
adduser deploy
usermod -aG sudo deploy
rsync --archive --chown=deploy:deploy ~/.ssh /home/deployFrom now on, connect as deploy.
Step 3: Install Node.js
Use nvm (Node Version Manager). It lets you install and switch between Node versions easily, which matters on long-lived servers.
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash
source ~/.bashrc
nvm install --lts
node -vStep 4: Deploy your app
Clone from Git (recommended):
git clone https://github.com/YOUR_USERNAME/YOUR_REPO.git
cd YOUR_REPO
npm install --productionCreate a .env file on the server. Never commit secrets to Git. SSH in and create your environment file:
nano /home/deploy/YOUR_REPO/.envAdd your variables:
NODE_ENV=production
PORT=3000
DATABASE_URL=your_database_url_hereStep 5: Keep your app running with PM2
Node.js apps crash. Servers reboot. PM2 is a process manager that keeps your app running, restarts it on crashes, and starts it automatically after reboots.
npm install -g pm2
pm2 start app.js --name "my-app"
pm2 startup
# Copy and run the output command
pm2 saveCheck your app's status:
pm2 status
pm2 logs my-appYour app is now running persistently on port 3000. But users shouldn't be hitting port 3000 directly. That's where Nginx comes in.
Step 6: Set up Nginx as a reverse proxy
Nginx sits in front of your Node.js app and forwards HTTP/HTTPS traffic to it. This gives you port 80/443 routing, SSL termination, static file serving, and the ability to add rate limiting and load balancing when you grow.
sudo apt update
sudo apt install nginx -y
sudo systemctl enable nginx
sudo systemctl start nginxConfigure the reverse proxy:
sudo nano /etc/nginx/sites-available/my-appPaste this configuration:
server {
listen 80;
server_name yourdomain.com www.yourdomain.com;
location / {
proxy_pass http://localhost:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
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_cache_bypass $http_upgrade;
}
}Enable the config and reload:
sudo ln -s /etc/nginx/sites-available/my-app /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginxStep 7: Point your domain and enable HTTPS
In your domain registrar, create an A record pointing your domain to your Droplet's IP. DNS propagation can take a few minutes to a few hours.
Then get a free SSL certificate with Certbot:
sudo apt install certbot python3-certbot-nginx -y
sudo certbot --nginx -d yourdomain.com -d www.yourdomain.comFollow the prompts. Certbot will automatically configure Nginx for HTTPS and set up auto-renewal. Your app is now serving traffic over HTTPS for free.
Step 8: Configure the firewall
Lock down your server. Only allow the traffic you need:
sudo ufw allow OpenSSH
sudo ufw allow 'Nginx Full'
sudo ufw enable
sudo ufw statusThis blocks everything except SSH and HTTP/HTTPS.
Bonus: Set up a DigitalOcean Managed Database
If your app uses PostgreSQL or MySQL, consider DigitalOcean's Managed Databases instead of running a database on the same Droplet. You get automated backups, one-click standby replicas, automatic security patching, and a connection pooler. It starts at around $15/month and saves a huge amount of operational pain. For any serious project, it's worth it.
Production checklist
Before you call it done, run through this list:
- Non-root user in use for deployments
- Firewall (UFW) enabled with only necessary ports open
- HTTPS enabled via Let's Encrypt
- PM2 startup configured so the app survives reboots
- Environment variables in
.env, not committed to Git - DigitalOcean Droplet monitoring enabled with alerting
- Automated Droplet backups turned on (about 20% of Droplet cost per month, absolutely worth it)
Going further
Once your app is stable, there are natural next steps depending on where you're headed.
For scaling up, DigitalOcean Kubernetes is great if you need to run multiple containers and want zero-downtime deployments. Their managed Kubernetes is significantly simpler to operate than rolling your own.
For faster deployments, DigitalOcean App Platform handles all of the above automatically (build, deploy, SSL, scaling) if you'd rather not manage a server at all. Just connect your GitHub repo and push. It's a great option for teams or projects where DevOps bandwidth is limited.
For AI workloads, check out Gradient AI, DigitalOcean's inference cloud, if you're building something with LLMs or GPU-intensive inference.
Wrapping up
Deploying to a real server teaches you things that managed platforms abstract away, and that knowledge makes you a better engineer. You now understand how Nginx proxies traffic, how PM2 keeps processes alive, how UFW filters connections, and how Let's Encrypt issues certificates.
That said, don't be dogmatic about it. Once you understand the fundamentals, using App Platform or Kubernetes to automate this stuff is completely reasonable.
If you're just getting started, DigitalOcean's free credit offer is a great way to experiment without risk. Spin up a Droplet, break things, learn, and delete it when you're done.