Most deploy tutorials cover one kind of app. Real servers rarely stay that tidy. A single DigitalOcean Droplet often ends up hosting a Node API, a static marketing site and a container or two.
This guide shows how to deploy all three on one Droplet, each on its own domain with HTTPS. Then we'll look at how ShipOwn handles the same mix from a desktop app.
Step 1: Create the Droplet
- Create → Droplets, choose a region near your users.
- Image: Ubuntu LTS.
- Size: a basic plan is enough to start; you can resize later.
- Authentication: SSH key (not a password).
- Optional: enable the free Cloud Firewall with inbound 22, 80 and 443.
Point three DNS records at the Droplet's IP, for example:
-
api.example.comfor the Node API -
www.example.comfor the static site -
tools.example.comfor the Docker app
Step 2: Base setup
ssh root@DROPLET_IP
adduser deploy && usermod -aG sudo deploy
rsync --archive --chown=deploy:deploy ~/.ssh /home/deploy
apt update && apt install -y nginx git certbot python3-certbot-nginx
ufw allow OpenSSH && ufw allow 'Nginx Full' && ufw enable
App 1: Node API under PM2
su - deploy
git clone https://github.com/you/api.git && cd api
npm ci && npm run build
PORT=3001 pm2 start npm --name api -- start
pm2 save && pm2 startup
Nginx (/etc/nginx/sites-available/api):
server {
server_name api.example.com;
location / { proxy_pass http://127.0.0.1:3001; proxy_set_header Host $host; }
}
App 2: Static site on Nginx
A Vite, Astro or plain HTML site doesn't need a running process at all. Build it and let Nginx serve the files:
git clone https://github.com/you/site.git && cd site
npm ci && npm run build # outputs dist/
sudo mkdir -p /var/www/site && sudo cp -r dist/* /var/www/site/
server {
server_name www.example.com;
root /var/www/site;
index index.html;
location / { try_files $uri $uri/ /index.html; }
}
No port, nothing to restart.
App 3: A Docker app
For anything not built on Node (a Python, Go or PHP service, for example), a container is the cleanest route.
curl -fsSL https://get.docker.com | sh
git clone https://github.com/you/tool.git && cd tool
docker compose up -d --build
Bind the container to localhost only, so it isn't exposed directly:
ports:
- "127.0.0.1:8080:8080"
server {
server_name tools.example.com;
location / { proxy_pass http://127.0.0.1:8080; proxy_set_header Host $host; }
}
Step 3: Enable sites and HTTPS
sudo ln -s /etc/nginx/sites-available/{api,site,tools} /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx
sudo certbot --nginx -d api.example.com -d www.example.com -d tools.example.com
That's three apps, three runtimes, three Nginx files and one certificate run. Now imagine updating each of them every week and rolling back when one breaks.
The same Droplet with ShipOwn
ShipOwn is a desktop app for Windows, macOS and Linux that deploys to Linux servers over SSH. It installs no deployment agent on your Droplet, and it supports exactly these three targets:
- Node apps run by PM2: Next.js, Nuxt, Remix, SvelteKit, NestJS, Fastify and plain Express, with port, health check and restart policy set.
- Static sites served by Nginx: Vite with React or Vue, Astro, or plain HTML, published to a release directory. No process, no port.
- Docker apps: a Dockerfile or Compose file built on the server and published behind Nginx. Images are labelled with the commit they came from, and the previous container is kept until the health check passes.
For each app you go through the same five steps: connect server, pick source, review the editable plan, watch the build, operate. Domains and HTTPS work the same way for all three. ShipOwn writes the virtual host, tests it before reloading, restores it if Nginx rejects it, and requests a Certbot certificate.
Managing existing containers
If the Droplet already runs containers you started by hand, ShipOwn can still help. It inspects each container, maps published and internal ports, shows CPU and memory, flags crash loops, and lets you start, stop or restart a container, open a shell inside it and read its logs.
Keeping a mixed server healthy
-
Watch disk. Docker images and old builds add up.
docker system dfanddu -share your friends. ShipOwn's server view shows disk usage at a glance. -
One Nginx mistake affects everything. Always
nginx -tbefore reload. ShipOwn does this automatically. - Know what's deployed. ShipOwn shows which commit each app runs and how far behind its branch it is.
Rolling back on a mixed server
Each app type rolls back differently when you do it by hand:
-
Node app: check out the previous commit, rebuild and
pm2 reload. That takes minutes, and the app is in a half-built state while it runs. -
Static site: keep the previous
dist/folder around and copy it back. - Docker app: keep the previous image tag and run it again.
ShipOwn gives all three the same model. Every deploy goes into its own release directory (or an image labelled with its commit, for Docker). The live pointer only moves after the build and health check pass. A deploy that fails restores the previous release by itself, and you can go back to a retained release later with one click. Database migrations and persistent data still need their own backup and recovery plan, whichever tool you use.
FAQ
Can ShipOwn deploy a Python or Go app? Through Docker, yes. There's no native Python or Go runtime; ship it in a container.
Does ShipOwn need Docker on the server? Only for Docker apps. Node and static apps run without it, and ShipOwn can install Docker for you if you need it.
Is there a free trial? Yes, 30 days, no card required.
Conclusion
One Droplet can comfortably host an API, a static site and a container. Doing it by hand teaches you how the pieces fit. Doing it every week is where ShipOwn saves time and mistakes.
Written by Vishvajeet Shukla, full stack developer and creator of ShipOwn.
Top comments (0)