Remote gateway: Docker, HTTPS, and private management
Build from a matching package and configure persistent storage, HTTPS, SSH pairing, and restart/rollback checks.
Developer accessWhen a remote gateway is useful#
Use a server-hosted gateway when you need a fixed HTTPS address or must accept business events while the computer is offline. The assistant still executes on its designated Onevium device. The gateway does not make an offline computer run; queued work has a deadline.
Interface screenshots use isolated example data; captions identify empty tokens and unsaved drafts. Follow the connection checks below to verify your deployment.
Prerequisite: obtain a matching deployment package#
Before continuing, have:
- The complete deployment source package matching the desktop version, including
server/gateway/Dockerfile,compose.example.yml, and the protocol/workers files referenced by the Dockerfile. - Working Docker Engine and Compose v2, server SSH access, a domain, and valid TLS certificates.
- A dedicated persistent directory and a server account authorized to manage it.
- A working Onevium installation and model connection.
If the package is unavailable, obtain it from the maintainer first. This guide does not invent a public image or require ordinary users to clone a private repository. The image name below is a local tag built on your server.
Run Docker commands from the package root on the server. Run the SSH forwarding command on the computer hosting Onevium.
Use origins for gateway settings: the gateway address, ONEVIUM_GATEWAY_PUBLIC_URL, and GATEWAY_URL in API examples contain only scheme, host, and optional port. If the interface shows https://gateway.example.com/api/v1 as the API address, use https://gateway.example.com for those values, removing /api/v1. The private management address is likewise only http://127.0.0.1:8430, without a management path.
Step 1: save remote mode and export the device public key#
In Onevium, open Channels → External Access → External access settings → Connection:
- Choose Remote gateway under Gateway location.
- Enter the planned
https://gateway.example.comgateway address without/api/v1. - Expand Advanced remote settings and choose Private connection via SSH. You can enter
http://127.0.0.1:8430as the planned private address and leave the token empty initially. - Save first. Changing mode pauses external access; only then select Export device identity.
- Upload
onevium-device-public.jsonto the server over a verified SSH connection.
Enter the remote gateway HTTPS address; the connection change takes effect after saving.
The file contains owner_id, device_id, and public_key, not a private key. Do not generate a substitute private key for the desktop executor. Export does not mean the device is paired, a listener is enabled, or a service is published.
Step 2: build a local image#
From the matching package root:
docker build -f server/gateway/Dockerfile \
-t onevium-gateway:preview-c2409f75f .
The Dockerfile uses Node 24.15.0 and builds dependencies inside the image. Do not rebuild native dependencies in the active desktop installation. A successful build produces an image, not a running deployment.
Prepare a new dedicated data directory. Check UID/GID using the server account that will also own SSH management access:
id -u
id -g
This example uses /srv/onevium-gateway. Verify it is not another service's directory before creating it with the appropriate permissions:
sudo install -d -m 700 -o "$(id -u)" -g "$(id -g)" /srv/onevium-gateway
Do not reinitialize or change ownership of an existing deployment. SQLite storage needs local/block storage with working file locks, not a shared multi-host database volume.
Step 3: configure Compose and environment variables#
Create gateway.env in the package root. Replace UID/GID with the preceding output, and use the real domain and paths:
GATEWAY_UID=1000
GATEWAY_GID=1000
ONEVIUM_GATEWAY_IMAGE=onevium-gateway:preview-c2409f75f
ONEVIUM_GATEWAY_DATA_PATH=/srv/onevium-gateway
ONEVIUM_GATEWAY_PUBLIC_URL=https://gateway.example.com
ONEVIUM_GATEWAY_ENABLED=1
ONEVIUM_GATEWAY_ACCEPTING_REQUESTS=0
ONEVIUM_GATEWAY_CALLBACK_HOSTS=
| Variable | Purpose |
|---|---|
GATEWAY_UID/GID | Match container file access with the SSH management account |
ONEVIUM_GATEWAY_DATA_PATH | Persistent host directory mounted at /data |
ONEVIUM_GATEWAY_PUBLIC_URL | Public HTTPS origin and expected public Host |
ONEVIUM_GATEWAY_ENABLED=1 | Explicitly start the server listener |
ONEVIUM_GATEWAY_ACCEPTING_REQUESTS=0 | Reject new work during setup while retaining management and existing-query paths |
ONEVIUM_GATEWAY_CALLBACK_HOSTS | Comma-separated exact callback hostnames; empty blocks all callbacks |
The supplied Compose file maps container 0.0.0.0:8420 to host 127.0.0.1:8420. Do not expose port 8420 directly to the public network.
The base Compose file does not forward the admission switch or callback allowlist. Create gateway.override.yml:
services:
gateway:
image: "${ONEVIUM_GATEWAY_IMAGE:?Set the local image tag}"
environment:
ONEVIUM_GATEWAY_ENABLED: "${ONEVIUM_GATEWAY_ENABLED:-1}"
ONEVIUM_GATEWAY_ACCEPTING_REQUESTS: "${ONEVIUM_GATEWAY_ACCEPTING_REQUESTS:-0}"
ONEVIUM_GATEWAY_CALLBACK_HOSTS: "${ONEVIUM_GATEWAY_CALLBACK_HOSTS:-}"
Do not skip this: adding unreferenced values to gateway.env does not automatically pass them into a container. Validate the merged configuration:
docker compose --env-file gateway.env -p onevium-gateway \
-f server/gateway/compose.example.yml -f gateway.override.yml config --quiet
Step 4: initialize identity and start with admission paused#
Replace the public-key mount with its real absolute server path:
docker compose --env-file gateway.env -p onevium-gateway \
-f server/gateway/compose.example.yml -f gateway.override.yml run --rm --no-deps --pull never \
-v /srv/onevium-device-public.json:/device-public.json:ro \
gateway init --identity-file /device-public.json
Expect Gateway identity initialized. No listener started. Initialization writes identity once. If the file already exists, inspect the existing deployment instead of deleting configuration to retry. Compose run runs a one-off initialization here without publishing the service ports.
Start the gateway:
docker compose --env-file gateway.env -p onevium-gateway \
-f server/gateway/compose.example.yml -f gateway.override.yml up -d --no-build --pull never gateway
Check private management and logs:
docker compose --env-file gateway.env -p onevium-gateway \
-f server/gateway/compose.example.yml -f gateway.override.yml exec -T gateway node dist/cli.cjs manage --path state
docker compose --env-file gateway.env -p onevium-gateway \
-f server/gateway/compose.example.yml -f gateway.override.yml logs --tail 50 gateway
The expected checks are management-state JSON and a startup log containing status: listening and public_management: false. These are checks to perform, not observations already made on your deployment.
Step 5: configure public HTTPS reverse proxying#
This example assumes Nginx is installed and running on the same server as the gateway. Add this server block to your site configuration and replace the domain and valid certificate paths:
server {
listen 443 ssl;
server_name gateway.example.com;
ssl_certificate /path/to/fullchain.pem;
ssl_certificate_key /path/to/privkey.pem;
client_max_body_size 20m;
location / {
proxy_pass http://127.0.0.1:8420;
proxy_http_version 1.1;
proxy_set_header Host $http_host;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_read_timeout 3600s;
proxy_buffering off;
}
}
Check syntax first and reload only after it passes. If validation fails, fix the configuration before reloading:
sudo nginx -t
sudo nginx -s reload
Preserve the public Host, forward WebSocket upgrade headers, and disable buffering for SSE. The official Nginx WebSocket guide explains explicit forwarding of upgrade headers.
This configuration is for the remote Docker gateway. Do not substitute the built-in gateway's loopback Host rule, and never proxy Onevium's original web/management port.
From an authorized client, check:
curl -sS -o /dev/null -w '%{http_code}\n' \
https://gateway.example.com/api/v1/management/state
Expect 404. Management is not public. Direct browser-script business requests carrying Origin are also rejected; call from your backend.
Step 6: establish private SSH management and pair#
The management socket is /data/control/gateway.sock inside the container, corresponding to /srv/onevium-gateway/control/gateway.sock on the host. The directory is mode 700; socket and control token are mode 600.
Retrieve the control token in a trusted server terminal:
docker compose --env-file gateway.env -p onevium-gateway \
-f server/gateway/compose.example.yml -f gateway.override.yml exec -T gateway node dist/cli.cjs management-token
The output is a sensitive private management token. Paste it only into Onevium's matching password field, not into URLs, source code, or log screenshots.
On the Onevium computer, open and retain this forwarding connection:
ssh -o ExitOnForwardFailure=yes -N \
-L 127.0.0.1:8430:/srv/onevium-gateway/control/gateway.sock \
gateway-user@YOUR_SERVER
Use a verified SSH host and key; do not disable host checking. The SSH user needs permission to access the host socket. The local endpoint explicitly binds loopback. The OpenSSH manual documents local-port forwarding to a remote Unix socket.
Return to Onevium's advanced remote settings:
| Field | Example |
|---|---|
| Gateway address | https://gateway.example.com |
| Management connection | Private connection via SSH |
| Private management address | http://127.0.0.1:8430 |
| Private management token | The retrieved control-token |
Private management detail: enter the address and token, save, then pair. The token is empty and the draft is unsaved in this example.
Save, then select Pair this device. Expect a paired confirmation. If it fails, inspect SSH and private management rather than switching to public /management. A public URL alone cannot manage a self-hosted gateway.
Step 7: publish, allow admission, and make a real call#
- In remote mode, create or select an application and configure its assistant and project capabilities.
- Run capability checks and publish the service after they pass. See external access.
- Change
ONEVIUM_GATEWAY_ACCEPTING_REQUESTSto1in servergateway.env, then rerun the precedingup -d --no-build --pull never gatewaycommand to apply the environment. - Select Enable external access on the Onevium application page and confirm connectivity.
- Use the application's key and authenticated subject to request a test reply. Save the admission receipt and query the actual Run result.
A 202, running container, or paired device alone is not a completed end-to-end check. Follow the same run_id to a real terminal state and the correct backend result. See conversations for API/SSE examples and authentication for credentials.
For callbacks, add exact hostnames to server ONEVIUM_GATEWAY_CALLBACK_HOSTS, reapply Compose, then configure a callback. Only public HTTPS/443 destinations are accepted; empty lists, private networks, and localhost do not bypass restrictions. See Webhooks and callbacks.
Verify restart recovery#
Stop test submissions and let existing runs reach terminal states, or complete necessary reconciliation, before restarting:
docker compose --env-file gateway.env -p onevium-gateway \
-f server/gateway/compose.example.yml -f gateway.override.yml restart gateway
Use restart for the same configuration. After changing environment or image, use this guide's up command to recreate the container. Mounted storage is retained; see the official up reference.
Check that:
- Private management remains readable and application/device identity is unchanged.
- The original App Key still reads old runs and conversations.
- Completed attachments still download with matching contents.
- Reconnection does not replay work that started but has an unknown outcome.
Unknown outcomes can remain in reconciling, pending_sync, or similar states until confirmed. An online server can accept and queue work within deadlines, but cannot make a sleeping computer execute it.
Pause, back up, and roll back#
- Pause the business sender and inspect active runs; confirm outcomes or record unresolved items.
- Set
ONEVIUM_GATEWAY_ACCEPTING_REQUESTS=0, reapply Compose, and verify that new work receives 503 while old results remain readable. - Pause external access in Onevium. For a consistent backup, stop the gateway and copy the entire persistent directory to a new restricted backup location.
docker compose --env-file gateway.env -p onevium-gateway \
-f server/gateway/compose.example.yml -f gateway.override.yml stop gateway
Include identity configuration, databases and companion files, private control credentials, and attachments. Do not copy only a live SQLite file, use down -v, or delete /data as a rollback procedure.
Before reverting an image, confirm compatibility with the data format. If unknown, remain paused and obtain a compatible build or matching snapshot from the maintainer. An older snapshot may omit executions and callbacks that happened afterward; reconcile them rather than replaying business side effects. After recovery, keep admission at 0 until identity, old data, and connections are checked.
Common failures#
| Symptom | First checks |
|---|---|
| Build cannot find files | Complete matching package and package-root build context |
| Init reports an existing file | Existing initialization; do not overwrite owner/device identity |
| Container exits after startup | Enabled flag, server-config.json, directory permissions, logs |
| Correct address returns 403 | PUBLIC_URL versus Host, proxy Host preservation, unexpected Origin |
| 502 or dropped WSS | Host listener, TLS proxy, Upgrade headers, timeouts |
| Private management 401/unreachable | control-token, live SSH forwarding, loopback address, socket UID/GID |
| Pair action disabled | Saved settings, pending edits, configured private address and token |
| 202 without completion | Online Onevium device, published capabilities, approvals, run state |
| Environment changes ignored | Override forwarding and an up/recreate rather than restart alone |
| Data missing after restart | Changed DATA_PATH or a different volume; do not hide it by initializing again |
Next steps and acceptance boundary#
Retain the image tag, deployment configuration, and backup location. Record real API, model execution, SSH, TLS, SSE, callback, and restart checks. Until then, describe the state as “deployment awaiting acceptance,” not “production live.”
Continue with authentication, business conversations, and Webhooks and callbacks.