Deploying a static Astro web site on DigitalOcean with Tailscale, Docker, and Caddy.

· 4 min read

This blog documents how I configured a new production-ready web server on DigitalOcean using their VPC and firewall tools, a custom Debian droplet, Caddy as a reverse proxy, and Tailscale for private access. I’ve done this setup a few times now, and this writeup is both a guide and a checklist for myself and others.

1. Create a new DigitalOcean project

Log in to DigitalOcean and create a new project for your site. This can help keep your account organized as you add more servers over time.

2. Configure a Firewall and tags

Go to Networking -> Firewalls. We’re going to create 2 firewalls:

  1. no-inbound: we’ll block everything except SSH from our IP addresses or an existing Droplet if you have one
  2. http-https: allow http/s through (e.g. for proxy servers)

Both firewalls will have these 2 allow rules:

  • SSH (port 22): only from your Tailscale subnet or home IP
  • Tailscale UDP ports (41641): optional but helpful

and then the HTTP/S one will have this rule:

  • HTTP (port 80) and HTTPS (port 443): open to all

When you spin up new Droplets in the future, make sure you add them to at least 1 firewall.

2. Launch a Debian droplet in the VPC

Use the “Create Droplet” wizard:

  • Choose Debian 12 x64
  • Select Basic or Premium CPU depending on needs
  • Attach it to the VPC created earlier
  • Add your SSH key
  • Enable monitoring
  • Set a hostname like web01.yourdomain

Once it’s deployed, you’ll get a public IP and private VPC IP.

4. Install and configure Tailscale

SSH into the server and install Tailscale:

curl -fsSL https://tailscale.com/install.sh | sh
sudo tailscale up

Use --advertise-tags=tag:prod or other tags if you plan to automate ACLs. Once connected, you can disable port 22 to the public entirely and only access via Tailscale IP.

To test, connect from your computer now:

hiro@Mac:~/git/edgar-test|main  ssh caddy-01
The authenticity of host 'caddy-01 (100.xx.xx.xx)' can't be established.
ED25519 key fingerprint is SHA256:Sx<redacted>.
This key is not known by any other names.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added 'caddy-01' (ED25519) to the list of known hosts.
Linux caddy-01 6.1.0-26-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.1.112-1 (2024-09-30) x86_64

The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.

Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
Last login: Sun Jul 27 21:51:28 2025 from 10.xx.xx.xx
hiro@caddy-01:~$
logout
Connection to caddy-01 closed.

5. Install Caddy and enable HTTPS

Install Caddy with systemd support:

sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | sudo tee /etc/apt/sources.list.d/caddy-stable.list
sudo apt update
sudo apt install caddy

Configure your site by editing /etc/caddy/Caddyfile:

yourdomain.com {
  reverse_proxy 127.0.0.1:3000
}

Reload the config:

sudo systemctl reload caddy

Caddy will automatically get a TLS cert from Let’s Encrypt.

6. Deploy your web app or static site

Place your app on the server and bind it to localhost:3000 (or any port you defined in the Caddyfile). Caddy will proxy incoming traffic securely and handle HTTPS for you.

7. Test and verify

  • Visit your domain in a browser
  • Run curl -v https://yourdomain.com
  • Check sudo journalctl -u caddy for any errors
  • SSH into the server using tailscale ssh web01

Make sure the public IP only exposes 80/443 and that Tailscale is active.

8. Bonus: lock down or harden

If everything works:

  • Remove public SSH access entirely in the firewall
  • Install fail2ban or UFW if desired
  • Enable auto-updates with unattended-upgrades
  • Back up your Caddy config and site data

← Back to all posts