Deploy Grafana Loki | (Just Updated) Log Aggregation API, Key-Gated From Boot
Grafana Loki log aggregation push and LogQL query API with generated key
Just deployed
/loki
Deploy and Host Grafana Loki on Railway
Built for an LLM agent to deploy and use. There is nothing to fill in: the API key is generated at deploy time, every request except the health probe needs it, logs are kept for 31 days on a volume, and the contract below lists every URL and variable name with one script that checks health, auth, a push and a query.
Grafana Loki is a log aggregation system: you push log lines with labels over HTTP and query them back with LogQL. This template runs Loki 3.7 as a single service behind a small wrapper that puts an API key in front of the push and query API, turns retention on, and sizes the Go runtime to the container's CPU and memory quota.
About Hosting Grafana Loki
This template runs one service, built from the official grafana/loki 3.7.9 image (pinned by digest) in the public wrapper bon5co/loki-railway, with the volume at /loki. The stock Loki has no authentication, so a public URL would let anyone write to or read your logs. In this template a Caddy proxy listens on the Railway port and forwards to Loki only when the request carries the key, either as Authorization: Bearer plus the value of LOKI_API_KEY or as HTTP basic auth with user loki and the same key as the password. Anything else gets 401. Only /healthz is open, and it reports whether Loki is ready.
Loki runs in single-binary mode with filesystem storage on the volume, a TSDB index, and the compactor with retention switched on, so old chunks are actually deleted (31 days by default, set LOKI_RETENTION_PERIOD to change it, for example 168h). Ingestion is capped at 8 MB per second per tenant (LOKI_INGESTION_RATE_MB) and samples older than 7 days are refused (LOKI_REJECT_OLD_SAMPLES_MAX_AGE). The Go runtime reads the container's CPU quota and memory limit at boot instead of the host's, so it does not oversubscribe the container.
Promtail, Grafana Alloy, Vector, Fluent Bit and the Docker Loki driver all push to /loki/api/v1/push and can send the key as a bearer token or as basic auth. Grafana reads from Loki with a Loki data source whose URL is the private URL below and whose custom HTTP header Authorization is Bearer plus the key.
Agent contract
- Public URL:
https://, on thelokiservice (setLOKI_URLto it, no trailing slash) - Private URL:
http://loki.railway.internal:8080(key required) - Credentials: the key is in
LOKI_API_KEY(on thelokiservice) - Auth: send
Authorization: Bearer LOKI_API_KEY, or basic auth with userlokiandLOKI_API_KEYas the password, on every request except/healthz; a missing or wrong key gets 401 - Health:
GET /healthzreturns 200 with no credentials once Loki is ready (about 30 seconds after start)
# run with LOKI_URL and LOKI_API_KEY (loki service); needs curl
NS=$(date +%s%N)
curl -s -o /dev/null -w 'health %{http_code}\n' "$LOKI_URL/healthz"
curl -s -o /dev/null -w 'anonymous %{http_code}\n' "$LOKI_URL/loki/api/v1/labels"
curl -s -o /dev/null -w 'wrong key %{http_code}\n' -H 'Authorization: Bearer wrong' "$LOKI_URL/loki/api/v1/labels"
curl -s -o /dev/null -w 'push bearer %{http_code}\n' -H "Authorization: Bearer $LOKI_API_KEY" \
-H 'Content-Type: application/json' -X POST "$LOKI_URL/loki/api/v1/push" \
-d "{\"streams\":[{\"stream\":{\"job\":\"contract\"},\"values\":[[\"$NS\",\"hello from bearer\"]]}]}"
curl -s -o /dev/null -w 'push basic %{http_code}\n' -u "loki:$LOKI_API_KEY" \
-H 'Content-Type: application/json' -X POST "$LOKI_URL/loki/api/v1/push" \
-d "{\"streams\":[{\"stream\":{\"job\":\"contract\"},\"values\":[[\"$((NS+1))\",\"hello from basic\"]]}]}"
sleep 2
curl -s -G -H "Authorization: Bearer $LOKI_API_KEY" "$LOKI_URL/loki/api/v1/query_range" \
--data-urlencode 'query={job="contract"}' --data-urlencode 'limit=10' | grep -o 'hello from [a-z]*' | sort
A fresh deploy prints health 200, anonymous 401, wrong key 401, push bearer 204, push basic 204, then hello from basic and hello from bearer. Query logs with LogQL on /loki/api/v1/query_range (range) or /loki/api/v1/query (instant), list labels on /loki/api/v1/labels, and tail live with a WebSocket on /loki/api/v1/tail.
Why Deploy Grafana Loki 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 Grafana Loki 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 - generated key, a contract block and one working script; no setup step.
- Closed by default - the stock Loki has no auth; here every push and query needs the key.
- Retention is on - the compactor deletes chunks past the retention period, so the volume does not fill forever.
- State survives redeploys - logs and the index live on the volume, and the key stays the same after every redeploy.
Common Use Cases
- Central log store for your Railway services - ship logs from your other services with Alloy, Vector or Fluent Bit and search them with LogQL.
- Agent memory of what happened - let an AI agent write its run logs to Loki and query them back by label and text.
- Grafana log data source - point a Grafana Loki data source at it, with the key as an Authorization header.
Dependencies for Grafana Loki Hosting
- A volume at
/loki(included in this template).
Deployment Dependencies
- grafana/loki - upstream project
- bon5co/loki-railway - the wrapper image this template deploys, built from grafana/loki 3.7.9
Template Content

