Install a hub on one server
Weik.io 2026.2Install the hub on one Linux server with weikio-backend init and Docker Compose, then sign in with two-factor and connect the weikio CLI.
On this page
A hub on one server runs the whole platform in one Docker Compose stack: the terminator that publishes port 443, the backend with the admin UI and the API, the API gateway, the message server that keeps the hub’s storage, the log, metric and trace stores, the backup service and the telemetry forwarder. This page installs it from the release files on weik.io. For three or five servers, see Install a hub on three or five servers.
Before you start#
The server#
The stack is tested on Ubuntu 24.04 LTS with Linux kernel 6.8, on x86-64. A hub as tested has 4 virtual CPUs and 8 GB of memory. Builds of integrations run isolated with Landlock on Linux 6.7 or later, and Ubuntu 24.04 has it on. On an older kernel the platform still installs and builds, with weaker build isolation, and doctor warns about it.
Disk#
The hub needs disk for the storage budget you give init and a quarter of it more, about 2 GB for the images, and room for the log, metric and trace stores, which take up to about 75 GB with their default caps. The tested disk of 160 GB has room for all of them with --disk-budget 40GiB and about 29 GB to spare. Storage and its budget has the numbers behind this.
Encrypted disks#
Docker’s data folders and the installation’s home sit on an encrypted disk, set up before Docker is installed. Encryption at rest has the step-by-step guide for Hetzner and bare metal.
Disk speed#
The message server waits until the disk has each change before it confirms it, so the speed of that disk sets how many events a second the hub stores. On the tested shape a server’s own solid-state disk completed such a write in about 1 ms and the hub stored about 600 events a second, and a network-attached disk took 5 to 9 ms and the hub stored 70 to 100 a second. Measure the disk once it’s mounted. The command prints the time of 1,000 writes, so divide it by 1,000. Remove the file afterwards:
$ dd if=/dev/zero of=/data/disk-speed bs=4k count=1000 oflag=dsync
$ rm /data/disk-speedDocker#
The stack runs on Docker Engine with the Compose plugin, Compose 2.20.2 or later. The stack is tested with Docker 29.9.0 and Compose v5.6.0. To limit the size of the containers’ own logs, put this in /etc/docker/daemon.json and run systemctl restart docker:
{ "log-driver": "json-file", "log-opts": { "max-size": "50m", "max-file": "3" } }Updates#
Bring the server up to date with its kernel before the install. When Ubuntu writes /var/run/reboot-required, restart the server first.
$ apt-get update && apt-get dist-upgradeNames#
Create two DNS records that point at the server: the backend’s name, such as acme.example, which serves the admin UI and the API, and the name of the published leaf address, such as nats.acme.example, which the sites link to. Later you add a record for each gateway hostname on the public entrance, such as api-test.acme.example.
Ports#
Only TCP 443 has to be reachable from outside. The terminator serves the admin UI, the API, the sites’ links and the APIs on that one port.
Create the home and master key#
Every command on this page runs as root, for example with sudo. The installation’s home is /opt/weikio, owned by uid 1654, the user every Weik.io image runs as. The master key seals every secret the platform keeps.
$ install -d -o 1654 -g 1654 -m 0700 /opt/weikio
$ head -c 32 /dev/urandom > /opt/weikio/master.key && chown 1654:1654 /opt/weikio/master.key && chmod 0400 /opt/weikio/master.keyDownload and check the release#
A release publishes images.env, with one line per image pinned by tag and digest, and SHA256SUMS. This block downloads both, checks the file against its checksum and gets the backend image from it. Put the release’s version in place of <version>, such as 2026.2.1:
$ curl -fsSLO https://weik.io/downloads/releases/<version>/images.env
$ curl -fsSLO https://weik.io/downloads/releases/<version>/SHA256SUMS
$ sha256sum -c --ignore-missing SHA256SUMS
$ IMG=$(grep '^WEIKIO_IMAGE_BACKEND=' images.env | cut -d = -f 2-)images.env: OKOn any other result, download both files again before you go on.
Write the stack#
weikio-backend init runs once in the backend image and writes the stack into /opt/weikio/deploy/compose/single. Give it the two names and the storage budget:
$ docker run --rm -u 1654:1654 -v /opt/weikio:/opt/weikio -e WEIKIO_HOME=/opt/weikio -e Weikio__Keys__Dir=/opt/weikio/keys \
-e WEIKIO_MASTER_KEY_FILE=/opt/weikio/master.key -e Weikio__Nats__Urls__0=nats://nats:4222 -e WEIKIO_BACKEND_IMAGE=$IMG \
$IMG weikio-backend init --public-base-url https://acme.example --public-leaf-url wss://nats.acme.example:443 --disk-budget 40GiBThe command prints the installation’s operator seed once. Save it, and a copy of master.key, outside this server and apart from the backup archives, which contain neither, because a restore needs both.
--disk-budget caps the message server’s storage at a size of at most 80 % of the disk under it. The installation’s own stores reserve about 10.2 GiB of it, each project 1 GiB and each environment about 3.1 GiB, so 40 GiB has room for three environments and 20 projects of the default size. You raise the budget later while the hub runs, as Storage and its budget shows.
Two choices are made only at the first init. When a site will run on this same server, add --hub-network-leaf-url nats://nats:7422 so that site links over the hub’s own network. When you want to try the platform on one host, --dev writes a stack with an agent called dev, the project sandbox and the environment devtest, which needs at least --disk-budget 15GiB.
Files of the stack#
Each file in the stack folder is either the platform’s or yours, never both. An upgrade writes the platform’s files again for the new release and keeps yours as they are.
| File | Whose | What it is |
|---|---|---|
docker- |
the platform’s | the stack |
upgrade. |
the platform’s | moves the stack to a release and back |
raise- |
the platform’s | raises the storage budget while the hub runs |
standby- |
the platform’s | keeps a standby hub restored |
weikio- |
the platform’s | the install record: every choice the stack was written from |
README. |
the platform’s | the stack’s own guide for this release |
. |
yours | the settings you change day to day |
docker- |
yours, once you create it | services and settings of your own |
Leave COMPOSE_FILE unset, so docker compose reads docker-compose.override.yaml together with the stack file.
Start the stack#
Point the two DNS records at the server, then start the stack in its folder. The comments say what each command shows:
$ cd /opt/weikio/deploy/compose/single
$ docker compose up -d
$ docker compose ps -a # nats healthy; infra and init exited 0; backend, apim, splitter, terminator, backup, forwarder and the three stores up
$ docker compose logs init # the first platform-admin API key and the set-password link, shown once
$ (umask 077; docker compose logs init | awk '/first platform-admin API key/ {print $NF}' > first-api-key)
$ test -s first-api-key && docker compose rm -f init
$ docker compose run --rm infra weikio-backend doctorThe first platform-admin API key is printed once and never stored. The two lines after logs init save it into first-api-key, a file only root can read, and remove the init container with its log once the file has the key.
doctor prints one line per check, starting with the result and the name of the check, followed by what it found. A line whose result is warn or fail ends with fix: and the command that fixes it, and any fail makes doctor exit with an error. About a minute after the start, once the names point at the server, the platform has its first certificates from Let’s Encrypt. Until then the admin UI serves a temporary certificate and browsers warn.
Sign in the first time#
Open the set-password link from the init log. The local admin chooses a password and enrols an authenticator app, whose code it gives at every sign-in from then on. The admin UI then shows 10 recovery codes once. Keep them apart from the platform, as you keep the master key, because each one signs the admin in once when the authenticator app is lost, as Two-factor sign-in describes.
Connect the CLI#
Install the weikio CLI on your own machine and sign in with the key from first-api-key. A key given on standard input is read from there and no browser opens. Remove every copy of the file once the login has stored the key.
$ curl -fsSL https://weik.io/install.sh | sh
$ weikio login https://acme.example < first-api-key
$ rm first-api-keyOn a machine with a browser, weikio login https://acme.example opens the hub’s approval page instead, where you approve the key.
Create a project and site#
Create a project, an environment and the gateway its APIs are served on, make them the context of the next commands, and enrol an agent for the server that runs the integrations:
$ weikio project create sales
$ weikio create env test --entity-store-bytes 512MiB
$ weikio gateway create test --env test --hostname api-test.acme.example --networks internet --default -y
$ weikio context set --project sales --env test
$ weikio agent enrol site-a --host linux-compose --via public --capabilities environment=test --out ./site-aThe enrolment writes the agent’s bundle into ./site-a, shown once. Copy the folder to the site’s server, run its setup script there as root, and then delete the folder on both machines, because it contains the agent’s credential:
$ scp -r ./site-a site-host:
$ ssh -t site-host 'cd site-a && sudo sh ./setup.sh'Once weikio get agents lists the agent as online, write the first integration and run it:
$ weikio init hello
$ weikio run ./helloweikio run follows the integration’s log lines until Ctrl-C, and the integration keeps running. Sites and agents covers the agent’s options, and Your first integration explains what you just ran.