Hosting my website on a VPS — A Practical Guide#

This post is both a story and a how‑to: how I set up and now host probablyworks.nl on my own VPS using Docker, Caddy, and Hugo.

If you want full control, HTTPS by default, and a setup that scales from a side project to something serious — this is for you.


Why host your own site?#

Static hosting platforms are great, until you want:

  • Full control over your stack
  • Custom routing or multiple apps on one server
  • Zero vendor lock‑in
  • To actually understand how your site is served

A VPS gives you all of that — and with modern tooling, it’s not scary anymore.


What we’re building#

End result:

  • A VPS running Ubuntu
  • Docker + Docker Compose
  • A Hugo static site (Hugo Book + custom landing page)
  • Caddy as the web server
  • Automatic HTTPS (Let’s Encrypt)
  • A real domain: probablyworks.nl

All reproducible. All documented.


1. Get a VPS#

Any provider will do. Requirements are modest:

  • Ubuntu 24.04 (other versions and distro’s will also work)
  • Public IPv4 address
  • SSH access

Once created, log in via the provider console (not SSH yet).


2. Enable SSH access#

Install and start OpenSSH:

sudo apt update
sudo apt install -y openssh-server
sudo systemctl enable --now ssh

Create a non‑root user:

adduser [username]
usermod -aG sudo [username]

From here on, everytime you see username, replace it with your own created username.

Add your SSH key to:

/home/username/.ssh/authorized_keys

Fix permissions:

chmod 700 /home/username/.ssh
chmod 600 /home/username/.ssh/authorized_keys
chown -R username:username /home/username/.ssh

Now log in as:

ssh username@YOUR_SERVER_IP

Once this works, disable root SSH login.


3. Install Docker#

sudo apt update
sudo apt install -y ca-certificates curl gnupg

sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg

echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu noble stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

sudo apt update
sudo apt install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin

Allow your user to run Docker:

sudo usermod -aG docker username
exit

Log back in and test:

docker run hello-world

4. Clone your website repo#

git clone git@github.com:YOURNAME/YOURREPO.git
cd YOURREPO

This repo contains:

  • Hugo source
  • A multi‑stage Dockerfile
  • A Docker Compose setup with Caddy

5. Configure Hugo for a subpath#

The site is served at:

https://probablyworks.nl/

Obvisouly, the website you’re on now. So in the next part, replace this website with your own.

So Hugo must know this.

In hugo.toml:

baseURL = "https://probablyworks.nl/"
languageCode = "en-us"
title = "Tom's playground"

theme = ["hugo-book"]

[params]
  BookSection = "blog"
  BookBaseURL = "/blog"
  BookSearch = true
  BookToC = true
  BookCustomCSS = ["css/custom.css"]

This is critical for Hugo Book to load CSS, JS, and navigation correctly.


6. Configure Caddy for production#

Your Caddyfile should look like this:

{$DOMAIN} {
    root * /usr/share/caddy
    file_server

    encode zstd gzip

    header {
        Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
    }
}

www.{$DOMAIN} {
    redir https://{$DOMAIN}{uri}
}

No manual TLS config. Caddy handles HTTPS automatically.


7. Point your domain to the VPS#

At your DNS provider:

A   probablyworks.nl     → YOUR_SERVER_IP
A   www.probablyworks.nl → YOUR_SERVER_IP

Remove any AAAA records unless you really want IPv6.

Verify from the server:

dig probablyworks.nl +short

8. Start the site#

Create a .env file next to docker-compose.yml:

DOMAIN=probablyworks.nl
ACME_EMAIL=admin@probablyworks.nl

Then run:

docker compose build --no-cache
docker compose up -d
docker compose logs -f

You should see Caddy obtaining certificates.


9. You’re live#

Open:

You now have:

  • A fully static site
  • Automatic HTTPS
  • A reproducible Docker setup
  • Zero platform lock‑in

Final thoughts#

This setup scales surprisingly far:

  • Add more sites behind Caddy
  • Add CI/CD with GitHub Actions
  • Add monitoring or backups
  • Move providers without changing your stack

Happy hosting.