Hugo is a static site generator written in Go, famous for building thousands of pages in seconds. You write content in Markdown, pick a theme, and Hugo produces plain HTML, CSS and JS files. Serving those from Nginx on a VPS is about as fast, cheap and secure as web hosting gets: there’s no database and no PHP, and very little to attack.

For the trade-offs between static and dynamic sites, see static vs dynamic websites. Prefer JavaScript tooling? See Astro.

1. Install Hugo

Install the extended edition, which is needed by many themes for Sass. On your computer or build machine:

# macOS
brew install hugo
# Debian/Ubuntu: distro packages can be old – use the .deb from GitHub releases
wget https://github.com/gohugoio/hugo/releases/download/v0.140.2/hugo_extended_0.140.2_linux-amd64.deb
sudo apt install ./hugo_extended_*_linux-amd64.deb
hugo version

(Use the current release.) Many themes also need Git, and some need Dart Sass or Node for asset pipelines.

2. Create a site with a theme

hugo new site mysite
cd mysite
git init
git submodule add https://github.com/adityatelange/hugo-PaperMod themes/PaperMod
echo "theme = 'PaperMod'" >> hugo.toml

Themes can also be added as Hugo Modules (Go modules). Check your theme’s docs.

Basic hugo.toml:

baseURL = 'https://example.com/'
languageCode = 'en-us'
title = 'My Site'
theme = 'PaperMod'

[params]
  description = 'Notes on servers and self-hosting'

3. Write content

hugo new content posts/first-post.md
---
title: "First Post"
date: 2026-10-14
draft: false
tags: ["servers"]
---

Hello from **Hugo**.

Preview locally with live reload:

hugo server -D        # -D includes drafts
# open http://localhost:1313

4. Build for production

hugo --gc --minify

The finished site is in public/. Upload that folder to the server.

5. Serve it with Nginx

On the VPS:

sudo mkdir -p /var/www/example.com
sudo chown -R deploy:deploy /var/www/example.com
server {
    listen 80;
    server_name example.com www.example.com;
    root /var/www/example.com;
    index index.html;

    location / {
        try_files $uri $uri/ =404;
    }

    error_page 404 /404.html;

    location ~* \.(css|js|png|jpg|jpeg|gif|svg|webp|avif|woff2)$ {
        expires 1y;
        add_header Cache-Control "public, immutable";
    }
}

Hugo fingerprints asset filenames (when the theme uses resources.Fingerprint), so long cache lifetimes are safe. Then add HTTPS:

sudo nginx -t && sudo systemctl reload nginx
sudo certbot --nginx -d example.com -d www.example.com

See Nginx server blocks, Let’s Encrypt and Nginx static file caching. Enable Brotli or gzip too. See Nginx compression.

6. Deploy

From your computer with rsync:

hugo --gc --minify && rsync -avz --delete public/ [email protected]:/var/www/example.com/

--delete removes files on the server that no longer exist in the build. See rsync.

Automatically with GitHub Actions on every push:

name: Deploy
on:
  push:
    branches: [main]
jobs:
  build-deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with: { submodules: true, fetch-depth: 0 }
      - uses: peaceiris/actions-hugo@v3
        with: { hugo-version: 'latest', extended: true }
      - run: hugo --gc --minify
      - name: Deploy with rsync
        env:
          SSH_KEY: ${{ secrets.DEPLOY_KEY }}
        run: |
          mkdir -p ~/.ssh && echo "$SSH_KEY" > ~/.ssh/id_ed25519 && chmod 600 ~/.ssh/id_ed25519
          ssh-keyscan -H example.com >> ~/.ssh/known_hosts
          rsync -az --delete public/ [email protected]:/var/www/example.com/

See GitHub Actions deploy to a VPS.

Tips

  • Images: use Hugo’s image processing (resize, WebP) in templates for fast pages.
  • Search: client-side search (Pagefind, Fuse.js) works without a server.
  • Forms and comments need external services or small self-hosted backends, since the site itself is static.
  • One small VPS can host many Hugo sites. Each is just a folder plus a server block.

Quick reference

Task Command
New site hugo new site mysite
New post hugo new content posts/name.md
Preview hugo server -D
Build hugo --gc --minify
Deploy rsync -avz --delete public/ user@host:/var/www/site/

Related: CDN for your website and website speed optimization.

  • Web Development
  • Static Sites
  • Nginx