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
crowdsec
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'sRAILWAY_PUBLIC_DOMAIN) - Private URL:
http://crowdsec.railway.internal:8080 - Credentials:
BOUNCER_KEY_DEFAULT(bouncer API key, generated, 32 characters);AGENT_PASSWORD(password of the machinerailway-agent, generated, 32 characters) - Auth: bouncers send
X-Api-Key: $BOUNCER_KEY_DEFAULTonGET /v1/decisions; agents log in withPOST /v1/watchers/login(machine_idrailway-agent,password) and send the returned token asAuthorization: Bearer TOKENonPOST /v1/alerts - Health:
GET /healthreturns 200 without authentication;GET /v1/decisionswithout a key returns 403 andPOST /v1/alertswithout 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
csclistep after deploy. - Persistent - a volume at
/var/lib/crowdsec/dataholds 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
| Variable | Purpose |
|---|---|
BOUNCER_KEY_DEFAULT | Generated API key of the bouncer DEFAULT. Changing it and redeploying rotates the key. Add BOUNCER_KEY_NAME for more bouncers. |
AGENT_PASSWORD | Generated password of the machine railway-agent. |
CROWDSEC_PUBLIC_URL | HTTPS address of the service. |
The LAPI listens on Railway's PORT (8080). AGENT_USERNAME is baked in as railway-agent.
Template Content
