Services

Table of Contents

This page contains notes for my future self and contains the actual steps I took to setup everything up.

List of services on this VPS

nginx https://eiswanderer.de
forgejo https://git.eiswanderer.de
ntfy https://ntfy.eiswanderer.de
gatus https://status.eiswanderer.de

nginx

Quick and dirty setup using Emacs for a simple static site.

doas pkg install nginx
doas sysrc nginx_enable=YES
doas service nginx start

doas mkdir -p /usr/local/www/mysite-src
doas mkdir -p /usr/local/www/mysite

doas chown -R ax:www /usr/local/www/mysite-src/
doas chown -R ax:www /usr/local/www/mysite

# after updating the nginx config
doas nginx -t
doas service nginx reload

put the org files into mysite-src, then org-publish outputs the html files into mysite.

The current org-publish settings are here as part of my Emacs config - it still uses TRAMP which I used at the very beginning, when this website was basically just one file with few lines of text. The TRAMP part especially feels like its getting too slow, but for now, I just keep using it.

Edit /usr/local/etc/nginx/nginx.conf, in the server section, just point to correct new location (root /usr/local/www/mysite was the only line I edited at the to publish the very first index page).

    server {
        server_name  eiswanderer.de www.eiswanderer.de;

        #access_log  logs/host.access.log  main;

        location / {
            root   /usr/local/www/mysite;
            index  index.html index.htm;
        }

        #error_page  404              /404.html;
        
        # ...
        # in conf.d, there is a .conf file for each jail,
        # ngnix acting as reverse proxy for those
        include /usr/local/etc/nginx/conf.d/*.conf;
        # ...
        # the rest is defaults!
    }

Don’t forget to doas service nginx reload.

nginx reverse proxy example: /usr/local/etc/nginx/conf.d/forgejo.conf

Ensure the root nginx.conf contains this line:

include /usr/local/etc/nginx/conf.d/*.conf;

The file for ntfy and the other services follows the same structure.

server {
    server_name git.eiswanderer.de;
    client_max_body_size 512m;
    location / {
        proxy_pass http://10.0.0.10:3000;
        proxy_http_version 1.1;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_read_timeout 120;
    }

    listen 443 ssl; # managed by Certbot
    ssl_certificate /usr/local/etc/letsencrypt/live/git.eiswanderer.de/fullchain.pem; # managed by Certbot
    ssl_certificate_key /usr/local/etc/letsencrypt/live/git.eiswanderer.de/privkey.pem; # managed by Certbot
    include /usr/local/etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
    ssl_dhparam /usr/local/etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
}

server {
    if ($host = git.eiswanderer.de) {
        return 301 https://$host$request_uri;
    } # managed by Certbot

    listen 80;
    server_name git.eiswanderer.de;
    return 404; # managed by Certbot
}

forgejo

TODO setup

Running in its own jail.

Mirror settings - change default sync intervals

The default sync interval is set to 24h and the min possible interval to 1h. I wanted to sync more often, which was not possible via my instances GUI.

The main config file in the forgejo jail is /usr/local/etc/forgejo/conf/app.ini. Two simple changes in the [mirror] section at the bottom of the file:

[mirror]
#DEFAULT_INTERVAL = 24h
DEFAULT_INTERVAL = 1h
#MIN_INTERVAL = 1h
MIN_INTERVAL = 10m

Don’t forget to # service forgejo restart.

ntfy

TODO setup the jail

Create admin user

ntfy user add --role=admin ax

Create standard user + write-only token

For good measure, a gatus user was created with appropriate permissions. A token was generated for use in gatus.yaml (in the gatus jail).

doas bastille console ntfy
# then run the following cmds as root user

# prompts for passwd, use long random one - not needed afterwards
ntfy user add gatus

ntfy access gatus homelab write-only

# show token again later: ntfy token list
ntfy token add --label="gatus alerting" gatus

# helpful cmds
ntfy user list
ntfy access gatus
ntfy token list gatus

verify no public user sign-up allowed

TLDR: set auth-default-access: "deny-all" in ntfy/server.yml

quickly grepping the config (from within the jail)

grep -nE '^#?\s*(auth-default-access|auth-users|auth-access|auth-tokens|enable-signup|listen-http):' /usr/local/etc/ntfy/server.yml

to see that we set deny-all and enable-signup is false (by default)

22:listen-http: "10.0.0.6:2586"
131:auth-default-access: "deny-all"
133:# auth-users:
134:# auth-access:
135:# auth-tokens:
300:# enable-signup: false

other things to check:

# Read the running server's config (show nothing about auth-default-access, but shows login is off)
curl -s https://ntfy.eiswanderer.de/config.js

# Anonymous publish from the internet — should return 403
curl -s -o /dev/null -w '%{http_code}\n' -d test https://ntfy.eiswanderer.de/homelab
# Anonymous subscribe from the internet — should return 403
curl -s -o /dev/null -w '%{http_code}\n' 'https://ntfy.eiswanderer.de/homelab/json?poll=1'

# the real token should still work
curl -H "Authorization: Bearer tk_..." -d test https://ntfy.eiswanderer.de/homelab
# also those should prompt for pw and return 200
curl -u ax -s -o /dev/null -w '%{http_code}\n' -d test https://ntfy.eiswanderer.de/homelab
curl -u ax -s -o /dev/null -w '%{http_code}\n' 'https://ntfy.eiswanderer.de/homelab/json?poll=1'

TODO gatus

(jailed)

TODO Let’s Encrypt

Author: ax

Created: 2026-09-09 Mi 22:56