richardnixon.dev/README.md
Richard Nixon 456e3e5614
Some checks failed
trivy / config-scan (push) Failing after 22s
trivy / image-scan (push) Failing after 21s
ci: add Renovate (autodiscover) and Trivy CVE scan workflows
- renovate.json: gitea platform, weekly schedule on Monday, pins
  ranges, manages docker-compose images + dockerfile + github-actions.
  Critical-infra packages get extra labels for review.
- .forgejo/workflows/renovate.yml: runs Mondays 04:00 UTC, autodiscover
  Richard/* so the same workflow covers both this repo and the Hugo
  companion. Requires RENOVATE_TOKEN secret (instructions in README).
- .forgejo/workflows/trivy.yml: daily 05:00 UTC + on-push. Scans both
  IaC configs in infrastructure/ and every image referenced in
  docker-compose.yml for HIGH/CRITICAL CVEs (fixable only).
- Pin Traefik (3.6.7) and traefik-crowdsec-bouncer (0.5.0) so Renovate
  has a baseline to bump.
2026-05-27 17:57:36 +02:00

11 KiB
Raw Permalink Blame History

richardnixon.dev

Self-hosted infrastructure (Traefik + Docker Compose) running every service on the richardnixon.dev apex and subdomains.

The public blog at richardnixon.dev is a static Hugo site — its source lives in a companion repo: Richard/richardnixon.dev-hugo. Deploys are driven by Forgejo Actions; the runner in this stack builds the site and writes directly into the blog-static bind-mount.

Architecture

graph TD
    DNS[Cloudflare DNS] --> Traefik[Traefik + Let's Encrypt]
    Traefik --> CrowdSec[CrowdSec IPS]

    Traefik --> Blog[Hugo blog-static<br>richardnixon.dev]
    Traefik --> WP[WordPress<br>richardemanu.com]
    Traefik --> LocFlow[LocFlow API<br>locflow.richardnixon.dev]
    Traefik --> EireScope[EireScope<br>eirescope.richardnixon.dev]
    Traefik --> Forgejo[Forgejo + runner<br>git.richardnixon.dev]
    Traefik --> Umami[Umami Analytics<br>analytics.richardnixon.dev]
    Traefik --> Authentik[Authentik SSO<br>auth.richardnixon.dev]
    Traefik --> Grafana[Grafana<br>status.richardnixon.dev]
    Traefik --> Portainer[Portainer<br>portainer.richardnixon.dev]
    Traefik --> VStatus[Valheim Status<br>valheim.richardnixon.dev]

    LocFlow --> LocFlowDB[(PostgreSQL)]
    Umami --> UmamiDB[(PostgreSQL)]
    Forgejo --> ForgejoDB[(PostgreSQL)]
    Authentik --> AuthDB[(PostgreSQL + Redis)]
    WP --> WPDB[(MariaDB)]
    Grafana --> Prometheus[Prometheus + Loki]
    VStatus --> Valheim[Valheim Server<br>UDP:2456-2458]

Services

Service Domain Description
Hugo blog (blog-static) richardnixon.dev Static blog (Hugo + PaperMod) — content in richardnixon.dev-hugo
Forgejo git.richardnixon.dev Self-hosted Git forge (Actions enabled, SSO via Authentik)
Forgejo runner (internal) CI runner for Hugo blog deploys
WordPress richardemanu.com Personal site
LocFlow locflow.richardnixon.dev Localization automation platform (REST API)
EireScope eirescope.richardnixon.dev OSINT dashboard
Umami analytics.richardnixon.dev Privacy-focused analytics
Authentik auth.richardnixon.dev SSO/OIDC provider
Grafana status.richardnixon.dev Observability dashboards
Portainer portainer.richardnixon.dev Docker management
Valheim Status valheim.richardnixon.dev Game server status page
Valheim Server valheim.richardnixon.dev:2456 Dedicated game server

Technology Stack

Edge

  • Traefik v3 as reverse proxy with Let's Encrypt SSL
  • CrowdSec as IPS with community threat intelligence + Traefik bouncer
  • Authentik for SSO/OIDC on admin-only routes

Public blog (richardnixon.dev)

  • Hugo 0.123.7 extended + PaperMod theme (pinned v8.0)
  • nginx:alpine (blog-static) serves /root/richardnixon.dev-hugo/public/ via bind-mount
  • Forgejo Actions runner builds on push and copies output into the volume

Observability

  • Prometheus, Grafana, Loki, Promtail, cAdvisor, Node Exporter as the monitoring stack

Host hardening

  • Fail2ban: SSH brute-force protection (3 attempts = 24h ban)
  • GeoIP: country-based filtering for SSH
  • CrowdSec: web + SSH attack detection

Project Structure

richardnixon.dev/
├── docs/
│   └── deployment.md
├── infrastructure/
│   ├── docker-compose.yml
│   ├── .env.example
│   ├── traefik/
│   │   ├── traefik.yml
│   │   └── dynamic.yml
│   ├── blog-static/
│   │   └── nginx.conf
│   ├── forgejo-runner/
│   │   ├── config.yml
│   │   └── entrypoint.sh
│   ├── prometheus/
│   ├── loki/
│   ├── promtail/
│   ├── grafana/
│   │   └── provisioning/
│   ├── crowdsec/
│   └── valheim-status/
└── README.md

Quick Start

Prerequisites

  • Docker and Docker Compose v2
  • Domain with DNS configured

Deployment

  1. Clone both repositories:
cd /root
git clone https://git.richardnixon.dev/Richard/richardnixon.dev.git
git clone --recurse-submodules https://git.richardnixon.dev/Richard/richardnixon.dev-hugo.git
  1. Configure environment:
cd richardnixon.dev/infrastructure
cp .env.example .env
# Edit .env with your credentials
  1. Start services:
docker compose up -d

Traefik provisions Let's Encrypt certificates on first start. Detailed steps live in docs/deployment.md, including Forgejo Actions runner registration.

URL Routes

Configured in infrastructure/traefik/dynamic.yml.

Path Backend Notes
/ blog-static (Hugo) Redirects to /pt-br/ (default content language)
/pt-br/*, /en/* blog-static (Hugo) Bilingual blog content
/feed.xml, /sitemap.xml, /robots.txt blog-static (Hugo) Static SEO/feed files

All other domains route by Host() rule to their respective services.

Environment Variables

See infrastructure/.env.example for the full list. Key categories:

Category Examples
Forgejo FORGEJO_DB_PASSWORD, FORGEJO_SECRET_KEY, FORGEJO_INTERNAL_TOKEN, FORGEJO_OAUTH2_JWT_SECRET, FORGEJO_RUNNER_REGISTRATION_TOKEN
LocFlow LOCFLOW_DB_PASSWORD, LOCFLOW_SECRET_KEY, LOCFLOW_UMAMI_WEBSITE_ID
EireScope EIRESCOPE_SECRET_KEY, EIRESCOPE_UMAMI_WEBSITE_ID
Authentik AUTHENTIK_SECRET_KEY, AUTHENTIK_DB_PASSWORD
Umami UMAMI_DB_PASSWORD, UMAMI_HASH_SALT
WordPress MYSQL_ROOT_PASSWORD, WP_DB_PASSWORD
CrowdSec CROWDSEC_BOUNCER_KEY
Grafana GRAFANA_ADMIN_PASSWORD
Valheim VALHEIM_PASSWORD, ADMINLIST_IDS, PERMITTEDLIST_IDS

Grafana Dashboards

URL: https://status.richardnixon.dev

Dashboard Description
VPS System CPU, memory, disk, load average, uptime
CrowdSec Security Active bans, alerts, attack types
Traefik Proxy Requests/s, latency, status codes
Network & Firewall Bandwidth, TCP connections, security events
Container Logs Real-time log viewer with search (Loki-backed)
Valheim Server Players online, server status, resource usage

Prometheus Metrics

Job Target Metrics
prometheus localhost:9090 Prometheus self-metrics
cadvisor cadvisor:8080 Container metrics
traefik traefik:8080 HTTP requests, latency
node node-exporter:9100 VPS system metrics
crowdsec crowdsec:6060 Security metrics
valheim valheim-metrics:3903 Game server metrics
forgejo forgejo:3000 Git forge metrics (repos, users, HTTP)

Dependency updates and CVE scanning

Two automated workflows run via Forgejo Actions:

  • Renovate (.forgejo/workflows/renovate.yml) — opens PRs every Monday with version bumps for Docker images, Forgejo Actions, Hugo submodules, and Hugo binary. Config in renovate.json at the root of each repo. Uses RENOVATE_AUTODISCOVER to handle both Richard/richardnixon.dev and Richard/richardnixon.dev-hugo from a single workflow.
  • Trivy (.forgejo/workflows/trivy.yml) — scans the compose images and IaC configs daily for HIGH/CRITICAL CVEs. Same scan also runs on every push that touches the compose file.

Required secret: RENOVATE_TOKEN

Both repos need a Forgejo PAT with write:repository scope:

  1. https://git.richardnixon.dev/user/settings/applications → Generate New Token (name: renovate, scope: write:repository).
  2. Add it as a secret named RENOVATE_TOKEN in each repo:
    • https://git.richardnixon.dev/Richard/richardnixon.dev/settings/actions/secrets
    • https://git.richardnixon.dev/Richard/richardnixon.dev-hugo/settings/actions/secrets
  3. Trigger a first run manually via the workflow dispatch button to confirm Renovate authenticates.

A "Dependency Dashboard" issue will be opened in each repo summarizing pending updates.

Forgejo Actions (Hugo blog CI)

The forgejo-runner service registers itself on first boot using FORGEJO_RUNNER_REGISTRATION_TOKEN and joins the forgejo-internal network so job containers can resolve the internal forgejo hostname for actions/checkout.

Config (infrastructure/forgejo-runner/config.yml):

  • container.network: infrastructure_forgejo-internal — without this, checkout fails to resolve forgejo.
  • container.docker_host: unix:///var/run/docker.sock — required value; "automatic" is not accepted by recent runner versions.
  • valid_volumes: ["/root/richardnixon.dev-hugo/public"] — the only host path the workflow is allowed to write to.

The deploy workflow itself lives in the Hugo repo at .forgejo/workflows/deploy.yml. Pushing to its main branch builds Hugo (downloaded fresh per run) and copies public/ into the bind-mount served by blog-static. End-to-end push → live is ~3060s.

Security

CrowdSec

Installed collections:

  • crowdsecurity/traefik
  • crowdsecurity/http-cve
  • crowdsecurity/linux
  • crowdsecurity/whitelist-good-actors

Country blocks (web traffic): RU, CN, KP, IR, UA.

Useful commands:

docker exec crowdsec cscli metrics
docker exec crowdsec cscli decisions list
docker exec crowdsec cscli alerts list
docker exec crowdsec cscli decisions add --ip 1.2.3.4 --duration 24h --reason "manual"

SSH Security

Protection Configuration
Fail2ban 3 attempts = 24h ban
GeoIP Country-based filtering
CrowdSec SSH brute-force detection

Valheim Server

Configuration

Setting Value
Server Name Emerald Realms
World Name EmeraldRealms
Ports UDP 2456-2458
Backups Every 12 hours, 7 days retention
Status Page valheim.richardnixon.dev

Status Page Features

  • Server online/offline indicator
  • Players online count with connection duration
  • World name and game version
  • Server uptime
  • Copy-to-clipboard server address

Admin Commands (F5 console in-game)

kick [name/steamID]   - Kick player
ban [name/steamID]    - Ban player
unban [steamID]       - Unban player
banned                - List banned players
save                  - Force world save
info                  - Server info

Managing Admins

Edit infrastructure/.env:

ADMINLIST_IDS=76561198012345678,76561198087654321
PERMITTEDLIST_IDS=76561198012345678,76561198087654321

Then restart:

docker compose up -d valheim --force-recreate

Common Commands

# Status
docker compose ps

# Logs
docker compose logs -f <service>

# Restart a service
docker compose restart <service>

# Rebuild a service that has a build context
docker compose build <service> && docker compose up -d <service>

# Pull image updates
docker compose pull && docker compose up -d

License

MIT License