Add StyloBot to an existing Docker Compose app
Use this when you already have an application in Docker Compose and want StyloBot to sit in front of it. Your application stays on the internal Compose network; StyloBot becomes the one public HTTP port.
This takes about ten minutes. Start in observe-only mode, confirm traffic is flowing, then choose an enforcement policy.
Before you start
You need:
- an existing
compose.ymlwith an application service; - the port your application listens on inside its container; and
- Docker Compose v2 (
docker compose, not the old standalone command).
The examples use an app service named app listening on port 3000. Replace those two values with yours.
1. Stop publishing your app directly
If your app has a ports: entry such as "3000:3000", remove it. Keep expose: instead. expose keeps the port reachable by other Compose services without making it public from the host.
services:
app:
image: my-company/my-app:latest
expose:
- "3000"
If another internal service needs to call the app, keep using http://app:3000.
2. Add the FOSS gateway
Add this service alongside your app. The gateway calls the upstream by its Compose service name, so do not use localhost for DEFAULT_UPSTREAM.
services:
stylobot:
image: scottgal/stylobot-gateway:latest
ports:
- "8080:8080"
environment:
ASPNETCORE_URLS: http://+:8080
DEFAULT_UPSTREAM: http://app:3000
depends_on:
- app
app:
image: my-company/my-app:latest
expose:
- "3000"
For a custom domain, point your existing reverse proxy, load balancer, or DNS target at the host port 8080. Do not publish both app and stylobot: that would let callers bypass detection.
3. Start in observe-only mode
Bring up the changed services:
docker compose up -d app stylobot
docker compose logs -f stylobot
Open http://localhost:8080/ (or your normal public URL) and confirm the app still responds through StyloBot. The live dashboard is available at:
http://localhost:8080/_stylobot/
Keep your normal application smoke tests. They now exercise the real public path: client → StyloBot → app.
4. Verify detection without blocking anyone
Run these from a second terminal:
curl -is -A "Mozilla/5.0 (Macintosh) Safari/605.1.15" http://localhost:8080/
curl -is -A "curl/8.4.0" http://localhost:8080/
curl -is -A "Python/3.13 aiohttp/3.11" http://localhost:8080/.env
You should see X-Bot-* response headers and the requests in the dashboard. At this point StyloBot is observing and classifying your traffic; your app behaviour should be unchanged.
5. Choose an enforcement policy
Once the dashboard looks right, add your policy to the gateway command or configuration. For example, this starts the gateway in production mode and slows likely automated traffic instead of blocking it outright:
stylobot:
image: scottgal/stylobot-gateway:latest
command: ["5080", "http://app:3000", "--mode", "production", "--policy", "throttle-stealth"]
ports:
- "8080:5080"
Use allow while tuning, throttle-stealth for a cautious first enforcement policy, or route-specific policies when you are ready to protect sensitive endpoints. See Configuration for the complete policy model.
Add the commercial layer
The FOSS gateway above is complete on its own. Commercial adds durable PostgreSQL-backed state, live editing, dashboard packs, and licensed capabilities. It uses the same topology: StyloBot remains the only public service in front of app.
- Keep the
appandstylobotservice names from the FOSS setup. - Add a PostgreSQL service (or use your existing managed PostgreSQL instance).
- Replace the FOSS image with the commercial gateway image supplied through your customer registry.
- Put the license token and connection string in
.env; never commit either value.
services:
stylobot:
image: ${STYLOBOT_GATEWAY_IMAGE}
ports:
- "8080:8080"
environment:
ASPNETCORE_URLS: http://+:8080
DEFAULT_UPSTREAM: http://app:3000
BotDetection__Commercial__LicenseToken: ${STYLOBOT_COMMERCIAL_LICENSE_TOKEN}
BotDetection__Commercial__Postgres__ConnectionString: Host=stylobot-db;Database=stylobot;Username=stylobot;Password=${STYLOBOT_DB_PASSWORD}
depends_on:
stylobot-db:
condition: service_healthy
app:
condition: service_started
stylobot-db:
image: pgvector/pgvector:pg16
environment:
POSTGRES_DB: stylobot
POSTGRES_USER: stylobot
POSTGRES_PASSWORD: ${STYLOBOT_DB_PASSWORD}
volumes:
- stylobot-db:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U stylobot -d stylobot"]
interval: 5s
timeout: 5s
retries: 10
volumes:
stylobot-db:
Create a local .env next to your Compose file:
STYLOBOT_GATEWAY_IMAGE=your-customer-registry/stylobot-gateway-enterprise:8.5.3
STYLOBOT_COMMERCIAL_LICENSE_TOKEN=replace-with-your-license-token
STYLOBOT_DB_PASSWORD=use-a-long-random-password
Add .env to .gitignore. For production, use your Compose secret mechanism or an external secret manager instead of a checked-in environment file.
Then start it and verify the same public path:
docker compose up -d
docker compose ps
docker compose logs -f stylobot
Troubleshooting
| Symptom | Check |
|---|---|
502 from StyloBot |
The upstream must be http://app:<container-port>, not localhost. Confirm the app is healthy with docker compose exec stylobot wget -qO- http://app:3000/. |
| App is still reachable without StyloBot | Remove the app's ports: mapping; use expose: only. |
| No dashboard data | Browse through the StyloBot port, then check docker compose logs stylobot. |
| Commercial gateway will not start | Confirm the PostgreSQL healthcheck is passing and the license token/connection string are supplied as environment variables. |
Next: How StyloBot works, Configuration, or choose a plan.