TPTito Pinardo Gutiérrez
← Back to the rack
Español
1U · TRAEFIKReverse proxy · automatic TLS · LXC In production

Slot 2 of 25 in the rack · In production

1U unit · Public entrance

Traefik · the only door

Surface reduced on 26-09-2026

A reverse proxy in its own container that receives all web traffic from the internet, adds the certificate and hands it out by name. Routes aren't edited by hand: they come from the same list Ansible uses to generate the rest of the homelab.

Why Traefik
Automatic Let's Encrypt certificates, routes in files that reload without downtime and a dependency-free binary: it fits in a small container with no Docker.
What it publishes
Only what has to be seen from outside: this site, messaging, media and games. Internal panels were taken off the internet and go through another door with two-factor auth.
How it's managed
A list of services in the infrastructure repo. Ansible generates one route per domain and Traefik picks it up on the fly.
  • 36.6 GBof unrotated log I caught in time
  • 91,000+requests from an «attack» that was my own bot
  • 10services taken off the internet in one day
  • 1service list for Traefik and the home dashboard

Its code, in the open Public version of the repo: no IPs or secrets.

Inside the door

Everything public comes in here and nothing else. Pick a flow to see how each request's destination is decided.

Hermesvisiting the unit

Click a part or pick a flow. If you came from a skill, the parts where I use it are already lit.

      A request to this site
      1. A visitor — A visitor opens this site.
      2. Cloudflare — Cloudflare gets it first and passes it home.
      3. MikroTik router — The router forwards HTTPS coming from the WAN to the Traefik container.
      4. HTTPS entry point — Traefik receives it at its entry point.
      5. TLS termination — It presents the domain's valid certificate and decrypts the request.
      6. Visitor's real IP — It recovers the visitor's real IP, trusting the header only if it comes from Cloudflare.
      7. Routes — It looks at the requested name and finds its route.
      8. Security headers — It adds the security headers.
      9. This site — The site answers from its container.
      10. Security headers — The response goes back encrypted with its headers.
      11. A visitor — And reaches the visitor.
      A certificate renews itself
      1. Renewal through DNS — A certificate has under 30 days left. Traefik starts the renewal.
      2. Cloudflare — It creates a temporary record in Cloudflare's DNS to prove the domain is mine.
      3. Let's Encrypt — Let's Encrypt checks the record and signs the new certificate.
      4. Renewal through DNS — Traefik removes the temporary record and stores the certificate.
      5. TLS termination — From the next connection, visitors get the new one. No restarts.
      6. Monitoring — In case something failed, Hermes' certificate watcher warns days ahead.
      I publish a new service
      1. Infrastructure repository — I add a line to the service list in the repo: domain and service.
      2. Ansible — Ansible generates the route file and the start-page entry, showing the change before applying it.
      3. Routes — If it's a server service, it creates its DNS record in Cloudflare by itself, proxied: no need to open the dashboard at all.
      4. Cloudflare — Traefik spots the new file and loads the route without cutting anything.
      5. systemd service — The service stays just as isolated: Traefik runs with its own user and permissions.
      6. HTTPS entry point — The route is live.
      7. Monitoring — And monitoring starts checking it.
      I retire a service
      1. Infrastructure repository — I delete its line from the service list in the repo. That's all I do.
      2. Ansible — Ansible compares the list with what's published.
      3. Routes — It deletes the route file that's no longer on the list: otherwise Traefik would keep serving it.
      4. Cloudflare — And it deletes its DNS record in Cloudflare. It only touches the records it created: the rest of the zone is left alone.
      5. HTTPS entry point — The door no longer has any entrance for that name.
      6. Monitoring — And monitoring stops checking it.
      Someone types «www»
      1. A visitor — A visitor types the address with «www».
      2. Cloudflare — Cloudflare passes it home.
      3. HTTPS entry point — Traefik receives it.
      4. Redirects — That variant's route is a 301 redirect to the canonical address.
      5. A visitor — The browser asks again, now for the right one.
      6. Routes — This time there is a route to the service.
      7. This site — And the site answers. Before it was declared in the list, that variant returned a 526 error.
      A scanner knocks on the door
      1. A visitor — A scanner tries well-known paths: config files, Git folders, admin panels.
      2. Cloudflare — It goes through Cloudflare, which appends the real IP to the header.
      3. HTTPS entry point — Traefik only trusts that header if the connection comes from Cloudflare's ranges.
      4. Visitor's real IP — And it takes the last IP in the chain: if the scanner sends a fake header, it gets nothing out of it.
      5. Access log — The request is logged with its real IP.
      6. CrowdSec — CrowdSec recognises the scenario, decides to block that IP and shares it with its community network.
      7. HTTPS entry point — The Traefik plugin enforces the decisions at the entrance: that IP no longer gets through.
      8. Monitoring — Every morning, a summary: what it detected, how many community-list IPs showed up and which might be false positives.
      Someone looks for the dashboard from outside
      1. A visitor — Someone tries to reach the Traefik dashboard from the internet.
      2. HTTPS entry point — The dashboard has no public name, so there isn't even a route from outside.
      3. Traefik dashboard — Even if there were, it only accepts the home network and the VPN.
      4. A visitor — Nothing gained. Internal panels go through another door, with two-factor auth.
      1. A visitor

        Anyone opening one of my sites.

      2. Cloudflare

        Proxy in front of the domains: hides my home IP and filters. It also runs the DNS where Traefik proves the domain is mine.

      3. MikroTik router

        Forwards the HTTPS coming in on the WAN, and only that, to the Traefik container.

      4. Let's Encrypt

        The authority that signs the certificates, free and renewable every 90 days.

      5. Infrastructure repository

        The list of published services lives here, versioned: which domain goes to which service.

        • Git
      6. Ansible

        Reads the list and writes one route per domain, creates its DNS record in Cloudflare and deletes routes and records that are no longer there. The same list generates the homelab's start page.

        • Ansible
      7. HTTPS entry point

        Traefik listens for HTTPS. Plain HTTP only exists to redirect to HTTPS.

        • Traefik
      8. TLS termination

        Traefik presents each domain's valid certificate and decrypts the request.

        • Traefik
      9. Visitor's real IP

        Behind Cloudflare, every request seems to come from Cloudflare. Traefik only trusts the real-IP header if it comes from Cloudflare's ranges.

        • Traefik
      10. Renewal through DNS

        To prove the domain is mine, Traefik creates a temporary record in Cloudflare's DNS. No extra port to open, and it works for wildcards.

        • Traefik
      11. Routes

        One route per domain, in its own file. Traefik looks at the requested name and picks the service.

        • Traefik
      12. Redirects

        Variants with or without «www» send you to the canonical address with a 301, declared in the service list itself.

        • Traefik
      13. Security headers

        HSTS and the other headers so the browser won't accept unencrypted versions.

        • Traefik
        • Linux / SSH hardening
      14. Traefik dashboard

        No insecure access, no public name and limited to the home network and the VPN.

        • Traefik
        • Linux / SSH hardening
      15. This site

        One of the services behind the door. It has its own unit.

      16. Media

        Plex and what surrounds it, to watch away from home.

      17. Messaging (Matrix)

        The chat server and its web client.

      18. Game websites

        The public site of the game servers. The game itself doesn't come through here: Traefik only speaks HTTP.

      19. Access log

        One line per request: who, what and the result. Rotated daily, 14 kept, compressed. It's what CrowdSec reads.

        • Linux (Debian, Alpine, Arch)
      20. CrowdSec

        Reads the access log and recognises scans, hunts for sensitive files, known exploits and brute force. It shares what it sees with its community network and gets its list of malicious IPs back.

        • CrowdSec
        • Linux / SSH hardening
      21. systemd service

        The official binary with its systemd unit, no Docker: fewer parts at the most exposed door.

        • Linux (Debian, Alpine, Arch)
        • Linux / SSH hardening
      22. Monitoring

        Monitoring checks it answers and Hermes' certificate watcher warns before any of them expires.

      What I use from my skill tree

      Pick a skill: the parts where I use it light up on the map and I tell you what for.

      Ansible
      Generates the routes and the start page from the same list, showing the change before applying it.
      LXC
      Runs in its own LXC container, separate from the other services.
      Git
      The list of published services is versioned: every new domain is a commit.
      Linux (Debian, Alpine, Arch)
      systemd service with its own user, log rotation with logrotate and a disk that no longer fills up.
      Traefik
      Entry reverse proxy: automatic TLS through DNS, one route per domain, redirects, security headers and real IP only from Cloudflare.
      Linux / SSH hardening
      HSTS headers, a dashboard with no insecure access limited to home and the VPN, and internal panels taken off the internet.
      CrowdSec
      Detection on the access log, the community list, a pinned Traefik plugin loaded from disk and a daily summary in Element.

      Incident log

      Real failures. Read the symptom, pick the cause you think it was and see how it was fixed.

      1. #1

        The door's disk at 80 %

        14-08-2026 · disk

        Checking the logs for something else, the Traefik container's disk was at 80 % and rising. If it filled up, about twenty domains would go down at once.

        What do you think was going on?

        Show the fix
        Cause
        There was no rotation set up for the access log. A 36.6 GB file on a 50 GB disk.
        Fix
        Daily rotation, 7 days, compressed. The first forced rotation tried to copy the whole file with only 9.5 GB free: I stopped it in time and emptied the original directly, which is safe because Traefik always writes at the end.
        Lesson
        Anything that writes nonstop needs rotation from day one. And the first clean-up of something huge needs thought: the normal method can be exactly what fills the disk.
      2. #2

        4,600 attempts a day against the game panel

        14-08-2026 · traffic

        The log showed thousands of requests a day against the game servers' panel. It looked like a brute-force attack and I was preparing an automatic block.

        What do you think was going on?

        Show the fix
        Cause
        The source IP was the home one. It was my Discord bot, asking the panel for its status every 10 seconds via the public domain: out to the internet, through Cloudflare and back in. Over 91,000 requests in a single session.
        Fix
        Have the bot talk to the panel over the internal network. The «attack» vanished from the log.
        Lesson
        Before blocking, look at where it comes from. Half the traffic mysteries are at home.
      3. #3

        CrowdSec was about to block Cloudflare

        15-10-2026 · CrowdSec

        Before switching CrowdSec on I looked at the access log: 95 % of requests came from a handful of IPs, all Cloudflare's.

        What do you think was going on?

        Show the fix
        Cause
        Behind Cloudflare, the connection always comes from Cloudflare. Without telling Traefik to trust its header, every visitor was logged with a Cloudflare IP, and CrowdSec would have blocked all of Cloudflare.
        Fix
        Traefik trusts the header only when the connection comes from Cloudflare's ranges, and CrowdSec and the plugin take the last IP in the chain, the one Cloudflare adds. Tested with a fake header: it doesn't fool them.
        Lesson
        Before letting something block, look at what it sees. As a bonus, the services behind it now get the real IP too.
      4. #4

        The «www» address gave a 526 error

        October 2026 · routes

        Anyone typing this site's address with «www» in front got a Cloudflare 526 error instead of the site.

        What do you think was going on?

        Show the fix
        Cause
        Cloudflare sent the «www» variant home, but Traefik had no route or certificate for that name, so the secure connection failed.
        Fix
        A new field in the service list to declare redirects: the «www» variant answers with a 301 to the canonical address.
        Lesson
        Every name pointing home needs its destination declared. If the redirect lives in the list, it gets generated and reviewed like everything else.

      Told in full in an article:

      How it was built

      1. Summer 2026

        One door for everything

        • Traefik in its container with the official binary, no Docker.
        • Automatic certificates through DNS with Cloudflare.
        • About twenty domains, each with its route.
      2. August 2026

        What I found reading the logs

        • A 36.6 GB unrotated log: daily rotation.
        • The «brute-force bot» was my own Discord bot.
      3. 26-09-2026

        Less surface

        • Internal panels off the internet: hypervisor, automations, start page, Traefik's own dashboard and others.
        • Their routes and DNS records deleted; they're still in use behind the internal door with two-factor auth.
        • Routes generated from a single list in the repo, with declared redirects.
      4. 15-10-2026

        CrowdSec at the door

        • Traefik sees each visitor's real IP: before, nearly all traffic seemed to come from Cloudflare.
        • CrowdSec reads the access log, shares with its community network and gets its list back.
        • Three days of only watching: it would have blocked 7,875 requests from 143 IPs, all scanners, with no known false positive. Then, blocking switched on.
        • Log rotation, in code.
      5. 18-10-2026

        On its own network

        • The public door moves to a separate network (DMZ), isolated from the home network: from there it only reaches DNS, monitoring and the services it publishes.
        • If someone compromised it, they wouldn't see the rest of the house.

      Decisions (and why)

      • Public proxy

        Traefik in a container, no Docker

        It's the most exposed part: the fewer layers, the better. The official binary with systemd is enough and Ansible updates it.

      • How domains are proven

        DNS-01 challenge on Cloudflare

        No need to open port 80 to the world, it works even if the service isn't ready yet and it allows wildcard certificates.

      • How many domains

        Three, each with its own use

        One for this site and the personal projects that can be seen in public, one for the games and one for the server's services. Keeping them apart makes it clear what's what. All three look after themselves, each with its own automation: on the server-services one, Ansible creates and deletes each route's record; on the games one, the AMP publisher creates each server's name when it starts and removes it when it stops; and the IP sync keeps the home IP up to date on all of them.

      • Blocking IPs

        CrowdSec, after a few days of watching

        Blocking a legitimate visitor (a friend in the games, me from outside) is worse than letting through a scan that finds nothing. Before blocking, the daily summary flags the IPs it would have blocked that got good responses: the false-positive candidates. The 71 that showed up were scanners hitting public pages.

      • What gets published

        Only what has to be seen from outside

        A published panel is an invitation. Internal ones go through Caddy and Authelia, with two-factor auth and only from home or the VPN.

      What I learned

      • The most exposed door is the one that should have the fewest parts.
      • Read the logs now and then: the disk and the bot both turned up that way.
      • Publishing is easy; unpublishing what isn't needed is what really cuts risk.

      What's next

      • Bring back automatic blocking based on the logs, now that my own noise is gone.
      • Move the common security headers into the service list, so every new route inherits them.

      Shall we talk?

      I can walk through any of these decisions in detail in an interview.

      Download CV Get in touch ← Back to the rack

      Rebuilt from my documentation: no domains, addresses or internal ports.