Guide

Install the Bloks server on a VPS with Docker

Run bloks-server on a VPS with Docker Compose: one .env, your own proxy or VPN, pairing your phone and desktop, sign-in with OpenID Connect, and backups.

Updated 6 November 2026

You want your agents on a machine that never sleeps, you have a small Linux server, and you would like it to stay yours: your proxy, your models, nothing sent anywhere you did not choose. Docker Compose gets you there in about thirty minutes, most of it the proxy.

By the end you will have bloks-server in a container running as its own account (never root), everything it keeps in one volume, and your desktop app and phone paired at an address of yours. For why you might want a server at all, see How to run AI agents 24/7.

What you need

  • A Linux server with Docker Compose 2.24 or newer.
  • An address your devices will use: a domain behind a reverse proxy or tunnel, or the server’s address on your VPN.
  • At least one engine for your agents: an OpenAI-compatible model server, an API key, or an engine such as Claude Code or Codex that you sign in to on the server.

1. Get the two files

Fetch compose.yml and the example settings into a folder of their own:

mkdir bloks && cd bloks
curl -fsSLO https://raw.githubusercontent.com/hamedgitty/bloks/main/compose.yml
curl -fsSL -o .env https://raw.githubusercontent.com/hamedgitty/bloks/main/.env.example

Or clone the repository and copy .env.example to .env.

2. Fill in .env

The one setting you need is where your devices will reach the server:

BLOKS_PUBLIC_URL=https://bloks.example.com

That is the address of the reverse proxy or tunnel you set up in step 4, or, on a VPN, the server’s address there with the port, such as http://100.101.102.103:8799 for Tailscale. Give both, separated by a comma, if you use both.

Everything else has a default. BLOKS_CLOUD=off is the default with Compose, so nothing goes to Bloks Cloud or bloks.dev. The port is published on the host’s loopback, 127.0.0.1:8799, for a proxy or tunnel on the same machine.

3. Start it

docker compose up -d
docker compose logs -f bloks

Compose pulls ghcr.io/hamedgitty/bloks-server. If it cannot, it builds the same image from the release, checked against the published sha256. The log should say it is listening, where your devices pair, and that Bloks Cloud is turned off. docker compose ps shows it healthy once it answers.

4. Put your door in front of it

Pick one way in. Bloks must be served from the root of the address, and replies stream, so response buffering is off and the read timeout is long. With Caddy, which gets the certificate itself:

bloks.example.com {
	reverse_proxy 127.0.0.1:8799 {
		flush_interval -1
	}
}

With NGINX, pass the original Host on (proxy_set_header Host $host), set proxy_buffering off and give it a long proxy_read_timeout. Cloudflare Tunnel and Tailscale work too, and the Docker notes in the repository have ready snippets for both. On Tailscale, use the IPv6 address from tailscale ip -6 if you want the iPhone to pair, since iOS treats it as a local network and the IPv4 one as not.

To check the door, open https://bloks.example.com/api/health on your device. It should answer with {"app":"bloks",...}.

5. Pair your devices

docker compose exec bloks bloks-server pair

This prints, for each address in BLOKS_PUBLIC_URL, a link carrying that address and a one time code, plus a QR code for the first. It works once, for five minutes.

  1. Desktop app: open Settings, Phone and devices, Your servers, paste the link and press Add server. The server becomes a workspace in the switcher, reached at that address with its own token.
  2. iPhone: scan the QR for an http:// address on your home network or your Tailscale IPv6 address. The iPhone app does not pair at an https:// address of your own yet.
  3. A terminal elsewhere: bloks-server token --server https://bloks.example.com --code 123456 trades the code for a token for bloks-server run, team and chat on another machine.

Once one device is paired, the next ones need no shell: Settings, Phone and devices, Pair a phone shows the same code, QR and desktop link. Remove a device there and its token stops working at once.

6. Point it at your models

For an OpenAI-compatible server, such as a self-hosted Qwen on vLLM, add to .env:

BLOKS_MODEL_URL=http://host.docker.internal:8000/v1
BLOKS_MODEL_NAME=Qwen
BLOKS_MODEL_TOOLS=1

then run docker compose up -d again. host.docker.internal is the host machine from inside the container. For Ollama, set OLLAMA_HOST=http://host.docker.internal:11434 and Bloks finds it with the models it has pulled. A hosted key in .env connects its provider: OPENROUTER_API_KEY, GEMINI_API_KEY, GROQ_API_KEY, DEEPSEEK_API_KEY or MISTRAL_API_KEY.

To sign in to Claude Code or Codex with an account, open a shell with docker compose exec bloks bash, then run claude or codex login. Run AI agents on local models goes deeper on custom endpoints.

Keeping Bloks Cloud off, or turning it on

With BLOKS_CLOUD=off, the server never contacts Bloks Cloud or bloks.dev. Your agents never needed it. What you give up is what Bloks Cloud carries: reaching the server from anywhere, bloks.dev/web, notifications and Live Activities on a locked phone, email to agents, WhatsApp, people in your workspace, Bloks AI and the Plan page. Telegram, Slack, Discord and Signal each connect out on their own and keep working.

If you want Bloks Cloud as well, set BLOKS_CLOUD=on in .env, run docker compose up -d, then:

docker compose exec bloks bloks-server activate blok_live_...

bloks-server pair then prints your own addresses first and a Bloks Cloud link after.

Sign in from a browser with OpenID Connect

The web door lets people open Bloks in a browser at an address of yours and sign in with the account they already have in Authentik, Keycloak, Authelia, Google or any OpenID Connect provider. It is off until you set BLOKS_WEB_URL, and there is no password of Bloks’s own.

The web door is a listener of its own, on port 8790, so give it an address of its own. Here it is https://chat.example.com.

  1. At your provider, make a confidential client with the redirect URI https://chat.example.com/auth/callback.

  2. Add the settings to .env:

    BLOKS_WEB_URL=https://chat.example.com
    BLOKS_OIDC_ISSUER=https://auth.example.com/application/o/bloks/
    BLOKS_OIDC_CLIENT_ID=...
    BLOKS_OIDC_CLIENT_SECRET=...
    BLOKS_OIDC_OWNER=...
    BLOKS_OIDC_ALLOWED_GROUPS=family
  3. Point your reverse proxy for that address at port 8790 over https, with buffering off, and restart with docker compose up -d.

  4. Open the address and sign in.

BLOKS_OIDC_OWNER is your own identity, which signs in as the owner. Invites point at BLOKS_WEB_URL/join, and Settings, Members manages people in the browser as it does in the app. Members come with Bloks Team, and the web door’s full list of settings is in the self-hosting notes in the repository.

Back up and restore

Make an encrypted backup and copy it out of the volume:

docker compose exec bloks bloks-server backup --encrypt
docker compose cp bloks:/home/bloks/.bloks-backups ./backups

A backup is the whole workspace in one file. Saved keys go only into a sealed (encrypted) backup; otherwise they are left out. Bloks also makes a daily backup on its own when nothing is running and keeps the newest seven.

To restore, run bloks-server restore FILE on the server. With Bloks stopped it does the whole thing; with Bloks running, it hands the file to the running server, which lets running replies finish, backs up the workspace as it is, then swaps in the restored copy on its next start. A restore is done on the server itself, never from a paired device.

Day to day

What How
Status docker compose exec bloks bloks-server status
Logs docker compose logs -f bloks
Update docker compose exec bloks bloks-server drain, then docker compose pull && docker compose up -d
Stay on a version BLOKS_VERSION=3.2.1 in .env
A shell docker compose exec bloks bash
Stop docker compose down (the volume stays)

An update never touches the volume. If pairing says the address cannot be reached, check /api/health first; if your proxy says “pair this device first”, it reached Bloks, and only paired devices get further, which is how it should be.

Questions

Does a Docker install send anything to Bloks Cloud?

No. BLOKS_CLOUD=off is the default with Compose, so nothing goes to Bloks Cloud or bloks.dev unless you turn it on in .env.

Can my iPhone pair with the server at an https address?

Not yet. The iPhone app pairs at an http address on your home network or your Tailscale IPv6 address. Until https pairing arrives, pair it on your VPN or turn on Bloks Cloud for it.

Do I lose my agents when I update the container?

No. Everything lives in one volume, and an update never touches it. Run bloks-server drain first so running replies finish.

Is the Bloks browser included in the Docker image?

No. The image does not include Chrome. Build an image of your own on top of it that installs Chromium, and the browser beside the chat runs on the server.

Can people sign in from a browser without the app?

Yes, through the web door. Set BLOKS_WEB_URL and an OpenID Connect provider such as Authentik, Keycloak or Google, and people sign in with the account they already have.

Meet your team.

Free for Mac, Windows and Linux. Bring the AI you already have.

View on GitHub