Migrating to 6.0
6.0 splits Octoprox into roles that share Postgres and Redis and never talk to each other. A single instance stays one process; a cluster runs gateways, workers, the api and a controller pair, each scaled on its own. The upgrade from 5.x is a short downtime: stop 5.x, start 6.0. The two never serve at once, and the way back is 5.0.11 on the same database, once one SQL statement has set its schema version back.
Why the way back is short
- Separate Redis keys. Every key 6.0 writes starts with
o6:, and 5.x’s have no prefix: 6.0 never reads a key 5.x left, and 5.0.11 never reads one of 6.0’s. - A migration that only adds. Migration
048adds the proxies’statusandstatus_changed_atcolumns and themetric_totalsandmetric_flushestables, and drops nothing: theversioncolumns 5.x writes stay. 6.0 records it inalembic_version, as 5.x records its migrations, and 5.x does not start on a revision it does not know; since 048 only adds, setting the version back to 047 with one statement is all 5.0.11 needs (see The way back). 048 runs online: each statement is a transaction of its own, waits at most half a second for a lock, and tries again after a pause when it has to wait (logged asMigration statement waiting for a lock; retrying), so 5.x processes still stopping at the switch can go on working on the database.
Before you start
- Take a backup, from Settings → Backup & Migration (tick Include history to keep the metrics, unless the install keeps a long observation log: 5.x builds the export whole in memory, every IP observation included), or a snapshot of the database, and keep it outside the install. The way back is one SQL statement and a redeploy (see The way back), so the backup is the last resort.
- Choose an internal secret: a long random string, different from
OCTOPROX_JWT_SECRET. It goes on every 6.0 api, gateway and controller, the same value on each (on a single instance, its one service). - Check Redis, unless it is the
redis:7-alpinethe shipped compose files run: 6.0 needs Redis 7.2 or later (redis-cli INFO servershows it asredis_version), and opens every connection withHELLO 3, so a proxy between Octoprox and Redis must passHELLOthrough. - Fetch the 6.0 image ahead, so its download is not part of the downtime: with Docker Compose, pull it while 5.x serves (below); on Kubernetes, pull it on the nodes the 6.0 pods will run on.
- For a cluster, decide where each role runs and how many of each (see Deployment & Scaling or Running on Kubernetes). Gateways need
OCTOPROX_INTERNAL_API_URL, the address they reach the api at; the api needsOCTOPROX_PROXY_PUBLIC_URLwhen clients reach the proxy at another host than the web UI. - Releases before 5.0.6. 6.0 brings their database through 047, the schema of 5.0.6 and later, to 048, and the way back sets it to 047, on which the release you came from cannot start. So the way back is 5.0.11, whatever release you came from.
- Plan the downtime. The proxy is down from the stop of 5.x until 6.0 has migrated the database and published its first routing feed, and on Kubernetes until the balancer has the gateways: seconds, or minutes when 048 fills the totals from a long metrics history. On a Docker Compose cluster the proxy can take up to about 10 s more to answer after 6.0 is ready: the gateways refuse connections while they start, and nginx leaves out for 10 s a gateway that refused three (
max_fails=3 fail_timeout=10s, as in 5.x). On a cluster with a small metrics history the switch took 25 to 28 s in all, fromdownto the first answered request.
With Docker Compose
Your compose file is where your settings live: each password, secret, address and port mapping you changed from the shipped placeholders is written in it and nowhere else. Never download a 6.0 file over it. 6.0 would start on the placeholders: with the shipped database password it cannot sign in to Postgres, which keeps the password the database was created with, and restarts in a loop; with the shipped JWT secret, which is public, anyone who reaches the API can sign their own admin token. Instead, while 5.x still serves:
-
Fetch the 6.0 files beside yours, and list what you changed in yours by comparing them with the stock 5.x files, which are the same in every 5.x release. A single instance (
docker-compose.ghcr.yml):curl -o docker-compose.ghcr.yml.new https://raw.githubusercontent.com/octoprox/octoprox/main/docker-compose.ghcr.yml curl -s https://raw.githubusercontent.com/octoprox/octoprox/v5.0.11/docker-compose.ghcr.yml | diff - docker-compose.ghcr.ymlA cluster (
docker-compose.cluster.ghcr.yml) takes the 6.0 nginx config too, since the role services have new names and the nginx upstreams changed with them:curl -o docker-compose.cluster.ghcr.yml.new https://raw.githubusercontent.com/octoprox/octoprox/main/docker-compose.cluster.ghcr.yml curl -o nginx/nginx.conf.new https://raw.githubusercontent.com/octoprox/octoprox/main/nginx/nginx.conf curl -s https://raw.githubusercontent.com/octoprox/octoprox/v5.0.11/docker-compose.cluster.ghcr.yml | diff - docker-compose.cluster.ghcr.yml curl -s https://raw.githubusercontent.com/octoprox/octoprox/v5.0.11/nginx/nginx.conf | diff - nginx/nginx.conf - Carry every change into the
.newfiles, and add the internal secret. A single instance has them all in its one Octoprox service. The cluster file keeps shared settings in blocks at its top, named in brackets below:x-octoprox-envfor every Octoprox service,x-postgres-envfor the roles with Postgres,x-apiandx-octoprox.OCTOPROX_INTERNAL_SECRET: the secret you chose (x-octoprox-env).- Your database password, in
OCTOPROX_DB_PASSWORD(x-postgres-env) and in thepostgresservice’sPOSTGRES_PASSWORD. OCTOPROX_JWT_SECRET,OCTOPROX_PUBLIC_URLand your other api settings (x-api); a Redis password, inOCTOPROX_REDIS_URL(x-octoprox-env) and in theredisservice; and any other setting you added, on the services of the roles that read it (see Settings per Role).- The port mappings you removed, such as Postgres 5433 and Redis 6379, and an image pinned to a release: pin the 6.0 one (
x-octoprox). - Your nginx changes, such as TLS or extra servers, into
nginx/nginx.conf.new, keeping its upstreams (api-1,api-2,gateway-1,gateway-2). - Leave
OCTOPROX_ROLEout if your 5.x file sets it: 5.x ignored it, and 6.0 runs only the role it names.
-
Pull the 6.0 image while 5.x serves:
docker compose -f docker-compose.cluster.ghcr.yml.new pullFor a single instance, the same with
docker-compose.ghcr.yml.new. From here onghcr.io/octoprox/octoprox:latestis 6.0 on this host, so a 5.x file you start again must pin:5.0.11.
A single instance
One container owns the ports, so a single instance switches with one restart. Swap the files, keeping yours for the way back, and recreate the containers:
mv docker-compose.ghcr.yml docker-compose.ghcr.yml.5x
mv docker-compose.ghcr.yml.new docker-compose.ghcr.yml
docker compose -f docker-compose.ghcr.yml up -d
up -d puts the Octoprox container on 6.0, and Redis on 6.0’s definition, without persistence: it starts empty, which 6.0 repairs on its own (see Redis loss and recovery). Postgres, the MITM CA and the IP databases keep their volumes. The redis_data volume 5.x kept Redis on is no longer used: once you stay on 6.0, remove it with docker volume rm <project>_redis_data (docker volume ls shows its name). In a checkout (docker-compose.yml), update the checkout, carry your edits of the compose file across the same way, then run docker compose up -d --build.
A cluster
Stop 5.x with its own file: the replicas, nginx, Postgres and Redis stop, and the volumes stay. Then swap the compose file and the nginx config, keeping yours for the way back, and start 6.0:
docker compose -f docker-compose.cluster.ghcr.yml down
mv docker-compose.cluster.ghcr.yml docker-compose.cluster.ghcr.yml.5x
mv docker-compose.cluster.ghcr.yml.new docker-compose.cluster.ghcr.yml
mv nginx/nginx.conf nginx/nginx.conf.5x
mv nginx/nginx.conf.new nginx/nginx.conf
docker compose -f docker-compose.cluster.ghcr.yml up -d
6.0 runs on the same postgres_data: the first api, controller or worker to start migrates it while the others wait for the lock. Redis starts empty on 6.0’s definition, without persistence, which 6.0 repairs on its own; redis_data is no longer used and can go once you stay on 6.0, as on a single instance. In a checkout (docker-compose.cluster.yml), stop 5.x with make cluster-down before you update the checkout, carry your edits across the same way, then start 6.0 with make cluster-up, which builds its image first.
On Kubernetes
The 6.0 roles run as Deployments of their own, under new names (see Running on Kubernetes): the switch deletes the 5.x Deployment and creates them, rather than turning a 5.x pod into a role.
-
Keep the MITM CA your clients trust. The gateways sign with it and the api serves its certificate, both from a Secret (see The MITM CA). If your 5.x pods mount it from a Secret, mount that one; if they keep it on a volume, copy it out of a 5.x pod before the switch, and create the Secret from the copy:
kubectl -n octoprox cp <a 5.x pod>:/app/data/ca ./ca kubectl -n octoprox create secret generic octoprox-mitm-ca \ --from-file=ca/octoprox-ca.crt --from-file=ca/octoprox-ca.key -
The switch. One release deletes the 5.x Deployment and creates the api, gateway, controller and worker Deployments under their new names. In the same release the proxy Service selects the gateways (and a tunnel Service the tunnel gateways), and the web UI’s Ingress or Service the api. The first api, controller or worker pod to start runs 048; give their startup probes room for it (see Probes), and a
helm upgrade --waita--timeoutthat covers it. The proxy is down until the gateways are ready (once they have the first routing feed) and the balancer has them.- Leave
--atomicoff on every release until one on 6.0 has deployed, fix-forward releases included. Until then the last deployed release is 5.x’s, and the automatic rollback would start 5.x on the migrated database, where 5.x does not start. If the release fails once 048 has run, run the way back’sUPDATEfirst, then roll back.
- Leave
After the checks, delete 5.x’s keys. 6.0 never reads them, but until they go, the way back finds them: 5.0.11 resumes with its proxy statuses and flushes the counts it left unflushed. A Redis that keeps no data loses them in a restart, which 6.0 repairs on its own; otherwise, on a Redis that holds nothing but Octoprox:
redis-cli --scan --count 1000 | grep -v '^o6:' | tr '\n' '\0' | xargs -0 -r -n 500 redis-cli unlink
It deletes every key without the o6: prefix, including keys whose names hold a space or a quote, as sticky bindings’ session IDs may. Until then the System page lists 5.x’s keys under Earlier releases in its Redis breakdown.
What migrates on its own
Migration 048 runs at the first start of a 6.0 api, controller or worker:
- Proxy status. A proxy 5.x was draining (it saved
draining_started_atwhen the drain started) comes back asdrainingsince then, so the drain finishes and the instance is terminated. Every other proxy startshealthy, and its first 6.0 health check, within onehealth_check_intervalof the start, corrects it. - Running totals. The
metric_totalstable is filled from the proxy, project and tunnel device history, so lifetime counters carry over exactly up to the migration.
Checks
Once 6.0 serves:
- Every 6.0 process answers
/readywith200. A gateway turns ready once the controller has published the routing feed, a few seconds after the controller starts. - Settings → System shows a controller leading, every gateway’s feed in sync, and the work streams draining: no stream whose queued entries stay above zero, and none with entries queued and no live worker taking its kind.
- Within a minute or two every proxy’s status reflects its first 6.0 health check. Until then a proxy has the status 048 gave it:
healthy, ordrainingfor a drain 5.x had started. - A request through the proxy is answered.
- A cloud proxy you deleted through 5.x just before the switch may be back: 5.x marked it terminating in Redis only, so if 5.x stopped before it terminated the instance, 6.0 sees a healthy proxy. Delete it again.
- A cloud instance 5.x was still creating when it stopped has neither a row nor the
octoprox-connectortag 6.0 finds instances by, so 6.0 never sees it, and it runs until you terminate it by hand: look for an instance whose name starts with the connector’sinstance_nameand that no proxy matches. - On the Connect page the proxy URL names the proxy’s host; if it names the web UI’s, set
OCTOPROX_PROXY_PUBLIC_URLon the api (the api logsThe Connect page names the web UI's host as the proxy'sat start when it is unset in a cluster). - A few lines in the first start’s logs are expected:
Database migrations completewithrevision048 from every api, controller and worker, withprevious047 from the one that migrated (an older revision from a release before 5.0.6) and 048 from the rest, andWaiting for the migration lockfrom any that started meanwhile;Routing feed head absent from Redis (a first start, or Redis lost its data); re-attributing every proxy, once, from the leading controller, since Redis holds no 6.0 routing feed yet; and, at info,Routing feed head missing; keeping the feed applied so farfrom a gateway that starts before the first publish. Nothing was lost: the re-attribution re-judges every proxy once against the IP databases. - On Kubernetes, then delete 5.x’s keys (see On Kubernetes).
What 5.x kept in Redis starts afresh, since 6.0 does not read it:
- Rate windows and quarantines: a proxy quarantined under 5.x is routed until it trips again.
- Sticky bindings: a sticky session may change exit once.
- Preflight verdicts: 6.0 echoes each preflight session once more.
- Failure examples and MITM captures: the Failures page’s examples and the captures start empty.
- Traffic blocks: a connector blocked under 5.x carries traffic again until the first metering window that carries its bytes asks for a decision, which blocks it again seconds later.
- Scale-in: nothing is scaled in for the first five minutes, while demand is measured again.
- Sign-in: a sign-in through an identity provider in progress at the switch fails; sign in again.
- What 5.x had not written is lost: the counts of its last minute, and the IP observations it had queued.
The way back
Stop 6.0, set the schema version back to 047, then start 5.0.11 on the same database. 6.0 recorded 048 in alembic_version, and 5.0.11, which brings the schema to its head at every start, stops at its start while the version says 048 (Can't locate revision identified by '048'). Setting it back is safe, since 048 only adds:
UPDATE alembic_version SET version_num = '047' WHERE version_num = '048';
It changes only a version of 048, so it never labels an older schema 047: a 6.0 start that stopped before 048 left the version where it found it, and 5.0.11 brings that schema to 047 itself. No 6.0 api, controller or worker may start after the statement: one that starts runs 048 again.
-
Docker Compose: stop 6.0 with its file, put the 5.x files back, pin their image to
ghcr.io/octoprox/octoprox:5.0.11(the cluster file names it inx-octoprox-base, a single instance in itsoctoproxservice), start Postgres alone, set the version, and start the rest. For a cluster:docker compose -f docker-compose.cluster.ghcr.yml down mv docker-compose.cluster.ghcr.yml docker-compose.cluster.ghcr.yml.new mv docker-compose.cluster.ghcr.yml.5x docker-compose.cluster.ghcr.yml mv nginx/nginx.conf nginx/nginx.conf.new mv nginx/nginx.conf.5x nginx/nginx.conf # Pin the image in docker-compose.cluster.ghcr.yml: ghcr.io/octoprox/octoprox:5.0.11 docker compose -f docker-compose.cluster.ghcr.yml up -d --wait postgres docker compose -f docker-compose.cluster.ghcr.yml exec postgres psql -U octoprox octoprox -c "UPDATE alembic_version SET version_num = '047' WHERE version_num = '048';" docker compose -f docker-compose.cluster.ghcr.yml up -dUse your database’s user and name in
psql. A single instance does the same withdocker-compose.ghcr.yml, without the nginx config. The 6.0 files are back as.new, ready for another try. In a checkout, stop 6.0 (make cluster-down, ordocker compose downfor a single instance), check outv5.0.11with your edits, start its Postgres and set the version as above (withdocker-compose.cluster.yml, ordocker-compose.ymlfor a single instance), and start it (make cluster-up, ordocker compose up -d --build). -
Kubernetes: set the version with
psqlagainst the database, then redeploy your 5.x release with the imageghcr.io/octoprox/octoprox:5.0.11, by ahelm rollbackto the last revision before 6.0 when that one ran it. The rollback replaces the 6.0 pods; should 5.x’s pods still stop at their start, a 6.0 pod started in between and ran 048 again: set the version again, and they start at their next restart.
5.x runs on the database as 6.0 left it, its version set back to 047: what 048 added is invisible to it. What 6.0 had not flushed is lost: the counts since its last metrics flush, up to a minute at the default OCTOPROX_METRICS_FLUSH_INTERVAL. 6.0’s keys, in a Redis that keeps them, harm nothing; delete them if you will not try again soon:
redis-cli --scan --count 1000 --pattern 'o6:*' | tr '\n' '\0' | xargs -0 -r -n 500 redis-cli unlink
Upgrading again. First reset what the first attempt left, while 5.x runs and no 6.0 process does. The next 6.0 start runs 048 again, since the version says 047, but 048 keeps the totals and proxy statuses it finds: without the reset, metric_totals misses everything 5.x flushed in between, and a drain 5.x started in between is not carried over:
SET lock_timeout = '2s';
DROP TABLE IF EXISTS metric_flushes, metric_totals;
ALTER TABLE proxies DROP COLUMN IF EXISTS status, DROP COLUMN IF EXISTS status_changed_at;
Run it again if a statement gives up waiting for its lock. On Docker Compose, docker compose -f <your file> exec postgres psql -U octoprox octoprox opens a session, with your database’s user and name.
The backup is the last resort, when the data itself went wrong. Find the release in the backup file (its envelope is plain JSON around the encrypted part, so head -n 6 octoprox-backup-<date>.opbak shows its app_version and schema_version without the passphrase), start that release on an empty database with the image pinned to it, sign in with the admin it seeds and import the backup from Settings → Backup & Migration, with Keep my current account unticked to get every user back as it was. The import refuses a backup whose schema_version differs from the install’s, so it takes that release or another at the same schema, not 6.0. A database snapshot goes back the way your Postgres restores one, with 5.0.11 started on it.
What changes
- Roles instead of identical instances. Every process runs the same image;
OCTOPROX_ROLEpicks what it runs:api,gateway,controller,worker,echo, orall(the default, everything in one process). See Architecture. - Gateways have no database. They route on a routing feed the controller compiles from Postgres and publishes to Redis, encrypted with a new shared secret,
OCTOPROX_INTERNAL_SECRET. - Proxy status is a column. Health, draining and terminating live on the proxy row in Postgres; 5.x kept them in Redis.
- Background work is jobs on streams. Health checks, syncs, cloud scaling, traffic decisions, metrics flushes, re-attribution and the rest are messages on a Redis stream per job kind: the controller publishes the periodic ones, any role asks for a run now, and workers take each message once through a consumer group. Nothing is sharded over a membership any more, and no process calls another.
- Counters are kept as running totals. Each proxy’s, project’s and tunnel device’s all-time counters are a row in Postgres, updated with every metrics flush.
A single instance needs nothing new to run: the compose files keep their service and run role all. Set OCTOPROX_INTERNAL_SECRET anyway, since the feed in Redis is encrypted with it.
Behaviour changes
Routing
least_usedpicks the proxy with the fewest transfers open on the gateway serving the request, and the connector with the fewest open transfers per unit of weight; ties are broken at random. It no longer reads cumulative request counts. See Routing Strategies.health_basedscores proxies on each gateway’s own outcomes, and keeps every proxy with fewer than ten outcomes on that gateway in the draw, so a new or restarted gateway tries every proxy before it prefers some.- The first request for a new country of an All countries pool waits up to 5 seconds while a worker creates the country’s sessions and the gateways receive them; 5.x created them inline.
- A connector whose custom or operator-installed provider was deleted or disabled is no longer routed; 5.x went on routing its stored proxies.
- A project whose stored routing strategy no router has is routed with
OCTOPROX_DEFAULT_STRATEGY(round_robinunless set) and logged; saving an unknown strategy is refused with400.
Health, attribution and rotation
- A health check’s verdict reaches every gateway within moments of the check.
- A pooled session slot that preflight refuses leaves the gateway that saw it at once, and every other gateway’s pool within seconds: a worker removes it, and the provider sync provisions a replacement.
- Re-attribution is queued.
POST /api/v1/geo/reattributeanswers202with the job’s key, and a worker re-judges. Saving the attribution settings re-judges only when the default conflict rule or city radius changed, and an IP database change only when it changes what is consulted (an enabled database added or removed, enabled or disabled, or its priority moved). - Detect runs on the api process, so the api needs network access to the proxies.
- A syncing connector is refreshed and reconciled when it is created or edited, when one of its proxies is deleted, and every
ip_refresh_interval; session pools are reconciled periodically too. - Cloud connectors: rotation times are drawn afresh at the first 6.0 run, and nothing is scaled in for the first five minutes after the switch (or after Redis is lost) while demand is measured again. Every instance 6.0 creates carries the tag
octoprox-connector, and a tagged instance that has no row is terminated within the hour; proxy credentials stay in the start-up script alone, as in 5.x (see AWS, GCP and Azure). - A failing proxy is logged once, when it turns unhealthy, rather than at every check.
Limits and quarantine
- Traffic-limit blocks are decided by a worker’s traffic job, asked for by each gateway metering window that carried a limited connector’s bytes, and decided again every minute while a connector is blocked. A connector can overshoot its limit by a few seconds of its traffic: a 5-second metering window, then the decision and the feed’s publish. After a usage reset or a limit edit the connector shows Pending until a worker has decided. See Traffic Limits.
- A rate-limit quarantine excludes the proxy at once on the gateway whose request tripped it, and on every other gateway within a fraction of a second.
API
POST /api/v1/projects/{project_id}/proxies/strategyis gone (405); set the project’srouting_strategy.- Clearing a project’s MITM captures (
DELETE /api/v1/projects/{project_id}/mitm/requests) and downloading a tunnel device’s config or profile need an editor; viewers get403. /healthanswers{"status", "role", "instance", "version"}, plusreasonswhen it answers503, which it now does while the process is not ready;proxy_countandhealthy_proxy_countare gone./readychecks the process instead of always answering200.GET /api/v1/projects/{project_id}/metrics/prometheusis Prometheus text (text/plain; version=0.0.4) rather than a JSON-encoded string.GET /api/v1/geo/databasesno longer returns a scheduled download’s credentials (update_auth);has_update_authsays whether it has any.GET /api/v1/system/statsdescribes the cluster: processes grouped by role, the controller, the routing feed and each gateway’s copy of it, and the work streams.runtime,cacheandworkersare gone. See the API reference.- A connector’s
traffic_usagegainspending, true from a usage reset or a limit edit until a worker has decided, and itsblockedis the block the gateways enforce. - The WireGuard and OpenVPN settings report every gateway carrying the tunnel instead of the process that answered; the IP database list and
/geo/statusreport every gateway. - A duplicate project name and an unknown strategy are
400(were500); a by-id route answers404for another project’s id; a refused edit leaves the stored row unchanged; a proxy upload adds its readable lines in one transaction, all of them or none. - Proxy and project counters are the all-time totals plus what the gateways counted since the last flush, and read the same on every api process.
- A JSON body must come with
Content-Type: application/json. A body sent without aContent-Typeis refused with422; 5.x read it as JSON. The web UI and thecurlexamples in these docs send the header; with Python’s requests, pass the body asjson=rather thandata=json.dumps(...). - Responses from the api, and from a single instance, carry
Vary: Origin. - A number in a response that is not finite (NaN or infinity) is returned as
null; 5.x answered500.
TLS interception
- The curl_cffi engine impersonates Chrome 150 and Firefox 147 (5.x: Chrome 142 and Firefox 144), so sites see those browsers’ fingerprints; Safari and Edge are unchanged.
- The curl_cffi engine verifies sites and
https://proxies against certifi’s CA bundle plus the OS certificate directory, as 5.x did, butREQUESTS_CA_BUNDLEandCURL_CA_BUNDLEno longer replace certifi’s bundle. A private CA added to the OS store is still trusted.
Tunnels
- A WireGuard subnet change is applied live on the gateways; 5.x needed a restart.
- Device changes (disable, rotate, remove) reach the gateways through the routing feed. With the controller or Redis down, a gateway keeps the devices it last applied until they return.
- Gateways compare their tunnel peers and sessions with the device list every 30 seconds and remove strays.
Elsewhere
- Every log line carries
rolenext toinstance, and the Redis URL is logged with its password masked. Credentials are never logged, at any level. - Postgres errors in the logs no longer carry the
<class 'asyncpg.exceptions...'>prefix or theDETAILline; change any log search or alert keyed on them. - A SOCKS5 upstream that cuts its refusal short, closing the connection after the reply code and the reserved byte, is counted as
upstream_rejectedon the Failures page, as a complete refusal already was; 5.x counted it asupstream_error. - A process that cannot bind its port, or whose app module cannot be found, exits with code 3 like any other failed startup (5.x exited with 1 for those two). An invalid setting, or a failed import inside the app, still exits with 1.
- Octoprox needs Redis 7.2 or later. Connections speak RESP3 (
HELLO 3; on Redis 5 every command fails) and introduce the client library withCLIENT SETINFO, which Redis before 7.2 refuses on every connection; the rate-limit quarantine and the work streams use commands Redis 6.2 added (ZADD GT,ZRANGE BYSCORE,XAUTOCLAIM). The shipped compose files and the tests runredis:7-alpine. - The web UI’s file serving no longer follows a path out of the build directory.
Settings and environment variables
New
| Variable | YAML | Default | Read by | Meaning |
|---|---|---|---|---|
OCTOPROX_ROLE |
all |
every process | What the process runs: all, api, gateway, controller, worker or echo |
|
OCTOPROX_INTERNAL_SECRET |
placeholder | api, gateway, controller | Encrypts and authenticates the routing feed, and signs gateway calls to the api. The same value on each | |
OCTOPROX_INTERNAL_API_URL |
http://localhost:8000 |
gateway | Where gateways reach the api, such as http://octoprox-internal-api:8000 |
|
OCTOPROX_WORKER_JOBS |
every kind | worker | Comma-separated job kinds this worker takes: health, sync, scale, provision, reattribute, enrich, traffic, metrics_flush, compaction, snapshot, geo_update, tunnel_last_seen, index_hygiene |
|
OCTOPROX_FEED_COMPILE_INTERVAL |
feed.compile_interval_seconds |
1 |
controller | The least time between two compiles of the routing feed: a change after a quiet spell is compiled at once, and changes that arrive sooner are compiled together |
OCTOPROX_RECONCILE_INTERVAL |
feed.reconcile_interval_seconds |
3600 |
api, gateway, controller, worker | How often every process reads in full what it follows, whatever the change stream announced: the net for a write that announced nothing |
OCTOPROX_HEALTH_CHECK_CONCURRENCY |
proxy.health_check.concurrency |
50 |
worker | Health checks one worker runs at once |
OCTOPROX_REDIS_REQUEST_TIMEOUT |
redis.request_timeout_seconds |
2 |
every role but echo | Seconds one Redis connect may take, and one call on a request’s path or right after a save, before it is given up (the request path then fails open); other replies may take up to 30 seconds |
Changed
OCTOPROX_INSTANCE_IDdefaults to the host name and a random suffix per boot (a pod or container name you recognise) instead of a random UUID.OCTOPROX_DB_APPLICATION_NAMEdefaults tooctoprox-<role>, so each role’s sessions can be told apart inpg_stat_activity. The shipped config files no longer set it.OCTOPROX_ROLEhad no effect in 5.x: every process ran everything, whatever it said. 6.0 refuses its old placeholder valuescontrolanddata, andworkernow runs the job runners alone, with no API and no proxy listener. Remove it from a 5.x setup before the switch, or set the role you mean.metrics.hosts.max_hostsandfailure_log.max_per_windoware per gateway rather than per instance.proxy.health_check.interval_seconds,proxy.ip_refresh_interval,database.metrics_flush_intervalandgeo.updater.check_interval_secondsare the periods at which the controller publishes the health, sync, metrics flush and IP database update jobs; set them on the controller.system.metrics_intervalis the snapshot job’s period: set it alike on the controller, the workers and the api, which charts the snapshots.
Removed: proxy.connection.max_retries (OCTOPROX_MAX_RETRIES), which nothing read. A config file that still sets it, or the long-unread proxy.health_check.enabled, unhealthy_threshold, healthy_threshold and proxy.connection.retry_delay_seconds, starts as before: unknown keys are ignored.
Removed: geo.observations.flush_interval_seconds (OCTOPROX_GEO_OBSERVATION_FLUSH_INTERVAL): workers ingest IP observations as they arrive. A config file or an environment that still sets it starts as before: unknown settings are ignored.
Removed: OCTOPROX_PROXY_PUBLIC_HOST and OCTOPROX_PROXY_PUBLIC_PORT (server.proxy_public_host, server.proxy_public_port), superseded by OCTOPROX_PROXY_PUBLIC_URL; 5.x read them while that was unset, 6.0 ignores them. Set OCTOPROX_PROXY_PUBLIC_URL=http://<host>:<port> on the api instead.
Removed: the standalone echo service. The octoprox-echo command and its OCTOPROX_ECHO_DATABASES, OCTOPROX_ECHO_TRUSTED_PROXIES, OCTOPROX_ECHO_HOST, OCTOPROX_ECHO_PORT and OCTOPROX_ECHO_CACHE_DIR are gone, as is port 8090. Run the image with OCTOPROX_ROLE=echo instead: it serves /echo on port 8000 and reads the same settings the api’s /echo reads:
docker run -d -p 8000:8000 \
-e OCTOPROX_ROLE=echo \
-e OCTOPROX_GEO_DATABASES='[{"path": "/data/GeoLite2-City.mmdb"}]' \
-e OCTOPROX_GEO_ECHO_TRUSTED_PROXIES='["10.0.0.0/8"]' \
-v /srv/geoip:/data:ro \
ghcr.io/octoprox/octoprox:latest
It needs no Postgres, Redis or secret, and it answers /live, /ready, /health and /metrics like every role.