← All posts

Deploy ASP.NET Core to DigitalOcean, the manual way and the one-click way

DigitalOcean’s Basic Droplets start at $6/month for 1 vCPU / 1 GB RAM / 25 GB SSD — enough for a single ASP.NET Core app. The server-side steps are the same Ubuntu setup as any other VPS (the Hetzner walkthrough covers each one in full detail); this is the DigitalOcean-specific path through it, what DigitalOcean does differently, and then the one-click version.

The manual way

Create the Droplet. In the DigitalOcean console: Create → Droplets, image Ubuntu 24.04, plan Basic → Regular ($6/mo, 1 GB RAM is enough for a small ASP.NET Core app), a region close to your users, and add your SSH key under the Droplet’s “Authentication” step so you get key-based login with no root password mailed anywhere.

Or from doctl, DigitalOcean’s own CLI, once you’ve added your key and noted its fingerprint (doctl compute ssh-key list):

doctl compute droplet create myapp \
  --region nyc1 \
  --image ubuntu-24-04-x64 \
  --size s-1vcpu-1gb \
  --ssh-keys <fingerprint>
ssh root@<droplet-ip>

Backups and snapshots, before you need them

Two DigitalOcean features are worth turning on during setup, not after something goes wrong. Droplet Backups (a checkbox at creation, or Droplet → Backups afterward) take a weekly image of the whole disk for 20% of the Droplet’s price — for a $6 Droplet, $1.20/month for a point-in-time restore of everything, OS included. Snapshots are the manual equivalent: take one before a risky change (an OS upgrade, a major framework version bump) and you can spin up a new Droplet from it if the change goes wrong, rather than rebuilding from scratch. Neither replaces application-level database backups (pg_dump or SQL Server’s own backup to the same S3 or Spaces bucket) — they protect the server, not a consistent point-in-time copy of live data.

Update, create a deploy user, lock down the firewall. Identical to any Ubuntu box:

apt update && apt upgrade -y
adduser --disabled-password --gecos "" deploy
usermod -aG sudo deploy
apt install -y ufw unattended-upgrades fail2ban
ufw allow 22/tcp
ufw allow 80/tcp
ufw allow 443/tcp
ufw enable
systemctl enable --now unattended-upgrades fail2ban

DigitalOcean’s own firewall, in addition to ufw. A Droplet’s ufw rules only take effect once the OS boots and the service starts; DigitalOcean’s Firewalls product (Networking → Firewalls) filters traffic before it ever reaches the Droplet, at the hypervisor level, and it’s the one that still protects you if ufw is ever misconfigured or disabled by accident. Create one, allow inbound TCP 22/80/443, and attach it to the Droplet — it costs nothing extra and it’s the belt-and-suspenders half of “database ports are never open to the internet.”

Install the ASP.NET Core runtime, publish, and copy the app — exactly the commands in the Hetzner guide’s steps 5 and 6: the dotnet-install.sh script, dotnet publish -r linux-x64 --self-contained false, and a releases/<timestamp> folder with a current symlink.

systemd unit and nginx site — also identical; see the Hetzner walkthrough for the full myapp.service file and the nginx proxy_pass block with the X-Forwarded-For / X-Forwarded-Proto headers. Nothing in either file is DigitalOcean-specific.

HTTPS. Point your domain’s A record at the Droplet’s IP, then apt install certbot python3-certbot-nginx && certbot --nginx -d myapp.example.com, same as any other Ubuntu box.

A reserved IP, so redeploying the Droplet doesn’t change your DNS. DigitalOcean Droplets get a new public IP if you ever have to recreate one (a resize across some plan changes, or rebuilding from a snapshot). A Reserved IP (Networking → Reserved IPs, free while attached to a Droplet) is a static address you point DNS at once and reassign to a new Droplet in seconds if you ever need to — cheaper insurance than finding out during an incident that your A record now points at a Droplet that doesn’t exist anymore.

What’s DigitalOcean-specific here

Everything above the runtime install is generic Ubuntu. The things actually worth doing differently on DigitalOcean are the cloud Firewall (defense in depth alongside ufw, not instead of it), a Reserved IP (so the Droplet’s lifecycle and your DNS are decoupled), and Backups (so a bad change is a restore, not a rebuild from memory). None of these are optional-but-nice on a $6 Droplet — they’re the few-minutes-each steps that turn “a server” into “a server that survives you making a mistake on it.”

DigitalOcean’s monitoring agent is a separate optional install (curl -sSL https://repos.insights.digitalocean.com/install.sh | sudo bash) that reports CPU, memory, disk and bandwidth into the console with free alert policies — worth adding on top of the ufw/ fail2ban/unattended-upgrades baseline above if you want a dashboard rather than SSHing in to run free -h when something feels slow.

What breaks (DigitalOcean-specific)

The Droplet’s private/VPC IP shows up where you expected the public one, if the region has VPC networking enabled by default and something binds to the wrong interface — ASPNETCORE_URLS should stay http://127.0.0.1:5000, loopback only, regardless of what other IPs the Droplet has; nginx is the only thing that needs to face the public interface.

A snapshot restore or a rebuild loses the deploy user and the systemd unit if you snapshotted before step 2 instead of after — take the snapshot once the full nine-step setup is done and working, not right after the base image boots.

The one-click way

The manual path above is roughly forty minutes the first time — and, more importantly, a source of small drift every time you set up the next server slightly differently: a firewall rule forgotten here, a systemd flag copied wrong there. None of it is hard in isolation; doing it identically, on every server, for as long as the app runs, is what turns into unpaid maintenance work. DotDeployer replaces all of it — from “Create the Droplet” through certbot — with three steps:

  1. Connect DigitalOcean. Paste a Personal Access Token scoped to your account (API → Tokens). It’s encrypted at rest and DotDeployer only ever calls the Droplet-management endpoints it needs.
  2. Pick a region and add your site from GitHub. DotDeployer creates the Droplet, applies the same hardening as above (deploy user, ufw, unattended upgrades, fail2ban), installs the matching .NET runtime, and configures nginx.
  3. Push. DotDeployer builds the release, issues the certificate the first time a domain is attached, and switches the current symlink with a health check first — the same release/rollback shape as the manual script, minus writing or maintaining it yourself.

The Droplet is still yours, in your own DigitalOcean account, billed to you directly — DotDeployer never touches your card for compute, it only automates what steps 1 through 9 above would otherwise have you do by hand, on every server, indefinitely.

Deploy it with DotDeployer

Connect your DigitalOcean account, add a site from your GitHub repo, and DotDeployer provisions the Droplet, hardens it, and configures nginx and HTTPS automatically -- then redeploys on every push.

Deploy to DigitalOcean with DotDeployer · Getting started