---
title: "Deploy CrowdSec | (Just Updated) Intrusion Prevention Local API for Bouncers, Keys Pre-Set"
description: "CrowdSec Local API server for bouncers, API keys and agent login preset"
category: "Other"
url: https://railway.com/deploy/crowdsec-or-just-updated-intrusion-preve
---

# 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 on Railway](https://railway.com/template/crowdsec-or-just-updated-intrusion-preve)**

Machine-readable deploy manifest (JSON, validated by TemplateCI): https://railway.com/deploy/crowdsec-or-just-updated-intrusion-preve/manifest.json

- **Creator:** SuperSlowSloth
- **Category:** Other

## Template content

### crowdsec

- **Image:** ghcr.io/bon5co/crowdsec-railway:latest@sha256:f3840b814029b5c892fbc1b33cc26f34cd4596ff1cfb95f94fb89fdd56bb0654
- **Health check:** /health
- **Public domain:** Yes

## Documentation

# 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)

```bash
# 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

- [CrowdSec repository](https://github.com/crowdsecurity/crowdsec)
- [CrowdSec documentation](https://docs.crowdsec.net)
- [Wrapper image source](https://github.com/bon5co/crowdsec-railway)

### 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`.


## Similar templates

- [Rocky Linux](https://railway.com/deploy/rocky-linux) — Hosted Rocky Linux 9 workspace with SSH and persistent storage. 🚀
- [Foundry Virtual Tabletop](https://railway.com/deploy/X5tR6G) — A Self-Hosted & Modern Roleplaying Platform
- [Letta Code Remote](https://railway.com/deploy/letta-code-remote) — Run a Letta Code agent 24/7. No inbound ports, just deploy.

Open this page in a browser: https://railway.com/deploy/crowdsec-or-just-updated-intrusion-preve
