Deploy CrowdSec | (Just Updated) Intrusion Prevention Local API for Bouncers, Keys Pre-Set

CrowdSec Local API server for bouncers, API keys and agent login preset

Deploy CrowdSec | (Just Updated) Intrusion Prevention Local API for Bouncers, Keys Pre-Set

Just deployed

/var/lib/crowdsec/data

Deploy and Host CrowdSec on Railway

Built for an LLM agent to deploy and use. The deploy form has no fields. CrowdSec, the open-source intrusion-prevention engine, comes up as a Local API (LAPI) decision server with a bouncer API key and a machine login already registered from generated secrets, a volume that keeps its database and configuration across redeploys, and a public HTTPS address. The contract below lists every address, variable name and one script that pushes a ban decision and reads it back the way a bouncer would.

CrowdSec here is the official crowdsecurity/crowdsec image, version 1.8.2. Agents report alerts and ban decisions to the LAPI; bouncers (firewall, nginx, Traefik, Caddy, Cloudflare, your own code) query it for the list of addresses to block.

About Hosting CrowdSec

This template runs one service from a wrapper image pinned by digest. The wrapper makes the LAPI listen on Railway's port, registers the machine login railway-agent (password AGENT_PASSWORD) and one bouncer per BOUNCER_KEY_NAME variable on every boot, and keeps the database, hub data and the whole /etc/crowdsec directory on the single volume mounted at /var/lib/crowdsec/data. To add a bouncer, add a variable BOUNCER_KEY_NAME with a random value and redeploy; the bouncer NAME then authenticates with that key.

This is the decision server only. The local log-parsing agent and the online API are switched off, so the community blocklist is not pulled and this service parses no logs by itself: remote CrowdSec agents (running next to the services you protect) push alerts to it, and bouncers read decisions from it. Anonymous machine registration on /v1/watchers is a CrowdSec upstream behaviour: unvalidated machines can register but cannot read decisions, so the only effect of abuse is rows in the database.

Agent contract

  • Public URL: CROWDSEC_PUBLIC_URL (https:// plus the service's RAILWAY_PUBLIC_DOMAIN)
  • Private URL: http://crowdsec.railway.internal:8080
  • Credentials: BOUNCER_KEY_DEFAULT (bouncer API key, generated, 32 characters); AGENT_PASSWORD (password of the machine railway-agent, generated, 32 characters)
  • Auth: bouncers send X-Api-Key: $BOUNCER_KEY_DEFAULT on GET /v1/decisions; agents log in with POST /v1/watchers/login (machine_id railway-agent, password) and send the returned token as Authorization: Bearer TOKEN on POST /v1/alerts
  • Health: GET /health returns 200 without authentication; GET /v1/decisions without a key returns 403 and POST /v1/alerts without a token returns 401 (write without key: 401)
# needs: CROWDSEC_PUBLIC_URL, AGENT_PASSWORD and BOUNCER_KEY_DEFAULT from the crowdsec service variables
API="$CROWDSEC_PUBLIC_URL"
curl -s -o /dev/null -w "health: %{http_code}\n" "$API/health"
curl -s -o /dev/null -w "read without key: %{http_code}\n" "$API/v1/decisions"
curl -s -o /dev/null -w "write without key: %{http_code}\n" -X POST -H 'Content-Type: application/json' -d '[]' "$API/v1/alerts"
TOKEN=$(curl -s -X POST -H 'Content-Type: application/json' -d '{"machine_id":"railway-agent","password":"'"$AGENT_PASSWORD"'"}' "$API/v1/watchers/login" | sed -n 's/.*"token":"\([^"]*\)".*/\1/p')
NOW=$(date -u +%Y-%m-%dT%H:%M:%SZ)
curl -s -w " push alert: %{http_code}\n" -X POST -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' -d '[{"scenario":"manual/ban","scenario_hash":"","scenario_version":"1.0","message":"manual ban 192.0.2.77","events_count":1,"start_at":"'"$NOW"'","stop_at":"'"$NOW"'","capacity":0,"leakspeed":"0","simulated":false,"events":[],"source":{"scope":"Ip","value":"192.0.2.77"},"decisions":[{"type":"ban","scope":"Ip","value":"192.0.2.77","duration":"4h","origin":"cscli","scenario":"manual/ban"}]}]' "$API/v1/alerts"
curl -s -w " ban list: %{http_code}\n" -H "X-Api-Key: $BOUNCER_KEY_DEFAULT" "$API/v1/decisions?ip=192.0.2.77"

A fresh deploy prints health: 200, read without key: 403, write without key: 401, then ["1"] push alert: 201 (the alert id), then the decision list for 192.0.2.77 with ban list: 200. The script pushes a 4-hour manual ban for the documentation address 192.0.2.77; running it again adds another alert and the decision query still returns 200.

Why Deploy CrowdSec on Railway?

Railway is a singular platform to deploy your infrastructure stack. Railway will host your infrastructure so you don't have to deal with configuration, while allowing you to vertically and horizontally scale it.

By deploying CrowdSec on Railway, you are one step closer to supporting a complete full-stack application with minimal burden. Host your servers, databases, AI agents, and more on Railway.

  • Agent-ready - no deployer fields, a contract block and one working push-a-ban-and-read-it-back script.
  • Keys pre-set - the bouncer key and machine login are generated and registered at boot; no cscli step after deploy.
  • Persistent - a volume at /var/lib/crowdsec/data holds the database and configuration, write-tested at every boot, and the image is pinned by digest.
  • Health-checked - Railway restarts the service if the API stops answering.

Common Use Cases

  • Shared ban list for several servers - agents on each host report attacks, every bouncer reads the same decisions.
  • Bouncers for web servers and proxies - nginx, Traefik, Caddy and firewall bouncers authenticate with one API key.
  • Decision API for your own code - push a ban from an application or an AI agent, query it from a middleware.
  • Central CrowdSec server for a homelab or small fleet - one decision server instead of one per host.

Dependencies for CrowdSec Hosting

  • None. Remote agents and bouncers are installed next to the services you want to protect.

Deployment Dependencies

Implementation Details

VariablePurpose
BOUNCER_KEY_DEFAULTGenerated API key of the bouncer DEFAULT. Changing it and redeploying rotates the key. Add BOUNCER_KEY_NAME for more bouncers.
AGENT_PASSWORDGenerated password of the machine railway-agent.
CROWDSEC_PUBLIC_URLHTTPS address of the service.

The LAPI listens on Railway's PORT (8080). AGENT_USERNAME is baked in as railway-agent.


Template Content

More templates in this category

View Template
Rocky Linux
Hosted Rocky Linux 9 workspace with SSH and persistent storage. 🚀

codestorm
51
View Template
Foundry Virtual Tabletop
A Self-Hosted & Modern Roleplaying Platform

Lucas
71
View Template
Letta Code Remote
Run a Letta Code agent 24/7. No inbound ports, just deploy.

Letta
51