Quick Start
Get Octopus API Gateway up and running in a few minutes.
Prerequisites01
Make sure you have installed Octopus — either the octopus binary or the
ghcr.io/xraph/octopus container image.
Basic setup02
Create the configuration
Create a file named config.yaml. The only required section is gateway with a listen address;
this config also declares one upstream and one route:
gateway:
listen: "0.0.0.0:8080"
workers: 0 # 0 = one worker per CPU core
upstreams:
- name: example-service
lb_policy: round_robin
instances:
- id: example-1
host: 127.0.0.1
port: 8081
routes:
- path: /api
methods: [GET, POST]
upstream: example-service
strip_prefix: /apiThis configuration:
Listens on
0.0.0.0:8080for HTTP traffic.Defines an upstream
example-servicewith one instance on127.0.0.1:8081.Routes
/api/*to that upstream, stripping the/apiprefix before proxying.
Validate, then start the gateway
Check the config parses and is valid, then run it:
octopus validate -c config.yaml
octopus serve -c config.yamlserve and validate take one or more -c/--config paths (each a file or directory).
serve also accepts -l/--log-level (trace, debug, info, warn, error):
octopus serve -c config.yaml --log-level debugTest the gateway
In another terminal, send a request and check the health probes:
# Proxied to example-service (path becomes /123 after strip_prefix)
curl http://localhost:8080/api/123
# Liveness / readiness probes (served on the listen port)
curl http://localhost:8080/livez
curl http://localhost:8080/readyz
# Prometheus metrics
curl http://localhost:9090/metricsConfiguration explained03
Gateway
gateway:
listen: "0.0.0.0:8080" # address:port to bind — required
workers: 0 # worker threads; 0 = one per CPU core
request_timeout: 30s # per-request timeoutgateway is the only required top-level section. See Gateway for
timeouts, body-size limits, probes, and compression.
Upstreams
upstreams:
- name: example-service # unique name, referenced by routes
lb_policy: round_robin # load-balancing policy
instances:
- id: example-1 # unique instance id
host: 127.0.0.1 # host or IP
port: 8081 # portSee Upstreams for instance weights, health checks, and circuit breakers.
Routes
routes:
- path: /api # path pattern to match (must start with /)
methods: [GET, POST] # allowed methods; empty means all
upstream: example-service # target upstream (must exist)
strip_prefix: /api # prefix removed before proxyingSee Routes for priority, prefix rewriting, and per-route auth.
Common scenarios04
gateway:
listen: "0.0.0.0:8080"
upstreams:
- name: user-service
instances:
- id: user-1
host: 127.0.0.1
port: 8081
- name: order-service
instances:
- id: order-1
host: 127.0.0.1
port: 8082
routes:
- path: /api/users
upstream: user-service
strip_prefix: /api
- path: /api/orders
upstream: order-service
strip_prefix: /apiupstreams:
- name: api-service
lb_policy: round_robin
instances:
- id: api-1
host: 127.0.0.1
port: 8081
health_check:
type: http
path: /healthz
interval: 10s
timeout: 2s
healthy_threshold: 2
unhealthy_threshold: 3gateway:
listen: "0.0.0.0:8080"
farp:
enabled: true
watch_interval: 5s
schema_cache_ttl: 5m
discovery:
backends:
- type: kubernetes
enabled: true
config:
namespace: default
label_selector: "app.kubernetes.io/part-of=myapp"
watch_interval: 10sgateway:
listen: "0.0.0.0:8443"
tls:
cert_file: /etc/octopus/tls/cert.pem
key_file: /etc/octopus/tls/key.pem
min_tls_version: "1.3"Environment variables05
Octopus expands ${VAR} references in the config file before parsing:
gateway:
listen: "${LISTEN_ADDR:-0.0.0.0:8080}" # default if LISTEN_ADDR is unset
auth_providers:
primary:
type: jwt
secret: "${JWT_SECRET}" # required — startup fails if unset${VAR} fails to load when VAR is unset; ${VAR:-default} falls back to the literal default.
Set them before running:
export JWT_SECRET=your-secret-key
octopus serve -c config.yamlRun with Docker06
Create docker-compose.yml
services:
octopus:
image: ghcr.io/xraph/octopus:latest
ports:
- "8080:8080"
- "9090:9090"
volumes:
- ./config.yaml:/etc/octopus/config.yamlStart it
docker compose up -d
docker compose logs -f octopusSee Docker deployment for the full image reference.
Graceful shutdown07
On SIGTERM (or Ctrl+C), Octopus flips readiness to 503, waits gateway.pre_stop_delay so
deregistration can propagate, then drains in-flight requests within gateway.shutdown_timeout
before exiting. See Graceful shutdown.
Next steps08
Troubleshooting09
Port already in use — change gateway.listen to a free port:
gateway:
listen: "0.0.0.0:8081"Connection refused to upstream — verify the backend is running and the instance host/port
are correct:
curl http://127.0.0.1:8081/healthzConfig rejected on startup — run octopus validate -c config.yaml; it reports the exact field
that failed (for example a route referencing an upstream that doesn't exist).
For runtime debugging via logs and metrics, see Observability. For task-focused walkthroughs, see the Guides.