I secured the web stack with CrowdSec, an open-source, collaborative Intrusion Prevention System (IPS)1. The integration with the existing Docker stack is tight: CrowdSec monitors the proxy logs in real time and blocks malicious traffic before it reaches the applications.

Here’s an overview of my updated architecture and how the pieces fit together.

My Website Architecture

The site is built with Hugo, a fast static site generator. For performance and security, docker-compose splits the stack into distinct layers:

  1. The Content Builder: A Hugo container (hugomods/hugo) that watches my source directory for changes. Whenever a post (like this one!) is written, it automatically builds the static HTML files to a ./public directory.
  2. The Web Server: A lightweight Nginx Alpine container that solely serves those statically built files. Fast, simple, and isolated.
  3. The Gatekeeper: I use Nginx Proxy Manager (specifically the zoeyvid/npmplus image) to handle SSL termination, manage domains, and act as my reverse proxy routing external traffic to my internal Nginx web server.

While Nginx Proxy Manager does a great job at routing traffic, I wanted a layer that would block brute-force attacks, port scanners, and malicious bots. CrowdSec fills that role.

Implementing CrowdSec

CrowdSec acts as my active defense layer. Instead of relying on static IP blocklists, CrowdSec analyzes my logs in real-time and shares threat intelligence with a massive global network. If an IP attacks another CrowdSec user anywhere in the world, my server knows to block it preemptively.

The Integration

The CrowdSec integration into the docker-compose file is clean. The implementation:

  • Log Parsing: Nginx Proxy Manager handles all inbound requests and writes access logs. I mounted these logs (./npm-data/logs) into the CrowdSec container as read-only. I set the LOGROTATE=true environment variable in NPM to ensure log files remain manageable.
  • The NPM Collection: I configured CrowdSec to use the ZoeyVid/npmplus collection. A collection in CrowdSec is a bundle of parsers and scenarios tailored for a specific service. This allows CrowdSec to instantly understand the Nginx Proxy Manager log format and identify common attack signatures.
  • The Bouncer: The zoeyvid/npmplus image ships with a CrowdSec bouncer built in. When the CrowdSec container detects malicious activity in the logs, it flags the IP, and the NPMplus bouncer queries CrowdSec and drops that traffic at the proxy level.

The Compose Configuration

My CrowdSec service definition in docker-compose.yaml looks like this:

  crowdsec:
    image: crowdsecurity/crowdsec:latest
    container_name: crowdsec
    restart: unless-stopped
    environment:
      - TZ=America/Chicago
      - COLLECTIONS=ZoeyVid/npmplus
    volumes:
      - ./crowdsec/data:/var/lib/crowdsec/data
      - ./crowdsec/config:/etc/crowdsec
      - ./npm-data/logs:/var/log/npm:ro

Looking Forward

Decoupling site generation (Hugo) from static serving (Nginx) and layering Nginx Proxy Manager with CrowdSec on top produced a resilient, performant, secure stack. Malicious bots drop at the edge, so server resources go to legitimate users.

I will report on how it performs, and I plan to contribute back to the CrowdSec network.


  1. CrowdSec. (2026). CrowdSec: Open-source, self-hosted, real-time network protection [Software]. https://github.com/crowdsecurity/crowdsec ↩︎