Shipixen Logo

Set Up a $4/mo Hetzner VM to Skip the Serverless Tax

Updated on
Set Up a $4/mo Hetzner VM to Skip the Serverless Tax

In the world of cloud computing, the costs can quickly add up, especially when using serverless architectures. However, with a little effort, you can set up a virtual machine (VM) on Hetzner for just $4 a month, allowing you to bypass the "serverless tax." and have full control over your server. In this tutorial, we'll walk through setting up a VM on Hetzner, installing Caddy for SSL, configuring Cloudflare for DNS, and deploying a web app using Github.

This tutorial is made for beginners ― you only need to be somewhat comfortable with the command line.
It is made as short as possible and avoids using e.g. Docker to keep things simple. Once you feel comfortable with this setup, you should consider using Docker for easier management or automatically deploying from Github with a webhook.

Let's do this.

Pre-requisites

Before diving in, make sure you have the following:

  1. Buy a domain: This is where every good idea starts.
  2. Generate an SSH key:
    • copy the .pub part somewhere handy.
    • remember the password you set during the key generation.
ssh-keygen -t ed25519 -C "your_email@example.com" -f ~/.ssh/id_ed25519_hetzner

This will allow secure access to your VM.

You need a domain to host your application and set up SSL. I like to use Cloudflare, but you can use any domain registrar you like. We'll use Cloudflare for it's security features later, so this'll make things easier.

Initial Setup

  1. Register on Hetzner Cloud and buy a server:
    • You can register quickly with a PayPal pre-purchase.
    • Use a company email rather than a personal one.
    • After creating the server, copy the IP address for later use.
  2. Set up on Cloudflare: If you don't have an account, create one.
    • Add a new site using the domain you purchased.
    • Add an A record with the name "@" and the IP address you copied from Hetzner.
    • Under SSL/TLS settings, ensure the encryption mode is set to "Full."

Why is this good? Cloudflare will cache your site and protect it from DDoS attacks and much more, free.
It must sound like they pay me. I wish. It's just a good service.

Configure your domain and add SSL with Caddy

SSL (https) is a must-have for any website. Caddy is a web server that automatically manages SSL certificates for you. It's easy to set up and use. Let's take a look.

  1. SSH into your server (login to your server)

    ssh -i ~/.ssh/id_ed25519_hetzner root@<server-ip-address>
    
  2. Install Caddy

    sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https curl
    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
    
  3. Enable on boot

    sudo systemctl start caddy
    sudo systemctl enable caddy
    
  4. Add domain; Open the Caddy config file

    nano /etc/caddy/Caddyfile
    

    And replace the contents with the below (note: port is set to 3000, you might want to change that):

    your_domain.com, www.your_domain.com {
      reverse_proxy localhost:3000
    }
    

    Replace your_domain.com with your domain.

  5. Restart to apply changes

    sudo systemctl reload caddy
    

Caddy automatically obtains and renews SSL certificates for your domain using Let's Encrypt. No extra steps are needed for HTTPS.

"Integrate" a Github Repo

We'll set up a simple "deployment" method that requires you to pull changes manually from GitHub. It's not the most efficient, but it does the job.

  1. Generate another SSH key, but this time on the Hetzner server. You should already be logged in otherwise, SSH into your server again.

    ssh-keygen -t ed25519 -C "your_email@example.com" -f ~/.ssh/id_ed25519_github
    
  2. Get .pub part of the SSH key & copy it to clipboard. Look at you go you're basically a pro now.

    cat ~/.ssh/id_ed25519_github.pub
    
  3. Add SSH key to Github

    • Go to Settings -> SSH and GPG keys
    • Press New SSH Key
    • Type a title e.g. "Hetzner" and paste the contents of
    • Press Add SSH key
  4. Now let's "tell" the server to use this key when pulling from Github. Open the SSH config file

    nano ~/.ssh/config
    

    And add the following:

    Host github.com
      HostName github.com
      User git
      IdentityFile ~/.ssh/id_ed25519_github
    
  5. Clone your repo

    git clone git@github.com:<username>/<repo-name>.git
    

That's it! Now you can cd <repo-name> and git pull to get the latest changes from your repo to "deploy" your app again.

[Bonus] Get an app running with automatic restart (Node.js)

You've made it this far, but what if you don't have an app to deploy? Let's set up a simple Node.js app that restarts automatically when it crashes.

Say we have this simple express app in app.js:

const express = require('express');
const app = express();
const port = 3000;

app.get('/', (req, res) => {
  res.send('Hello World!');
});

A bit low on energy?

Deploy a beautiful web app, landing page and blog with a click (serverless 🙈).
No? Okay. Let's get back to self-hosting. Had to try.
  1. Install node & npm (if not already installed)

    sudo apt update
    curl -fsSL https://deb.nodesource.com/setup_lts.x | sudo -E bash -
    sudo apt install nodejs
    

    (Verify by running node -v)

  2. Install pm2 (process manager)

    sudo npm install -g pm2
    
  3. Run app

    pm2 start app.js --name "my-node-app"
    
  4. Save & set up to run at startup

    pm2 save
    pm2 startup
    

    Now open your_domain.com in your browser and you should see "Hello World!"

  5. Check status and logs via:

    pm2 status
    pm2 logs
    

If the server crashes for any reason, pm2 will restart it automatically. Pretty neat, huh?
Remember: this should be running on port 3000, as per the Caddy config we set up earlier.

Wrap-up

You've set up a $4/mo Hetzner VM with SSL, DNS, a domain, and a web app. You've also learned how to deploy changes from Github and set up a simple Node.js app that restarts automatically.

This is not rocket science and it's a great starting point for hosting your own web apps. Once you get comfortable with this setup, you can add Docker so you can replicate the environment easily or set up a webhook to automatically deploy changes from Github.

Is there something specific you'd like to learn more about? Did we miss a tool?
How about setting up multiple servers? What about a SSR app like Next.js?

Reach out on Twitter/X or reach out on this website with your feedback!

Tools

You might also want to check out: