Step 1: Install Docker Engine 26 or newer
The docker.io package in Ubuntu's own repositories can lag behind, and the version matters here. Use Docker's install script, which sets up Docker's apt repository and installs the current Engine with the Compose plugin:
sudo apt update && sudo apt install -y curl openssl
curl -fsSL https://get.docker.com | sh
sudo usermod -aG docker $USER
Log out and back in so the group change applies, then check both numbers the installer will later check for you:
docker version --format '{{.Server.Version}}'
docker compose version
The first line must read 26 or higher. A LumaDock VPS deployed from the Docker template skips this step, since Docker comes preinstalled, though the version check is still worth running on any server you did not set up yourself.
Step 2: Download the installer and prepare .env
Rakazo's installer is a single shell script. Run it with --prepare-only first, so it downloads the Compose file, writes .env with random secrets and stops before pulling anything:
mkdir -p ~/rakazo && cd ~/rakazo
curl -fsSLO https://raw.githubusercontent.com/elie222/rakazo/main/infra/compose/install-images.sh
bash install-images.sh --prepare-only
You now have docker-compose.images.yml and a .env in which five values were generated for you with openssl rand: POSTGRES_PASSWORD, BETTER_AUTH_SECRET, ENCRYPTION_KEY, SCREEN_PROXY_SECRET and SANDBOX_SUPERVISOR_TOKEN. Leave them alone. The secrets checklist asks that the four long ones stay independent of each other, and ENCRYPTION_KEY in particular must never change later, because it encrypts every stored credential.
Open .env and change the public origin lines. Out of the box they point at http://127.0.0.1:5173, which is right for a laptop and wrong for a server:
BETTER_AUTH_URL=https://rakazo.example.com
WEB_ORIGIN=https://rakazo.example.com
API_URL=https://rakazo.example.com
RAKAZO_HOST=rakazo.example.com
All three origins must be the same HTTPS URL, and RAKAZO_HOST must be the bare hostname, because the web container's preview server only answers for hosts on its allowlist. Leave it at localhost and the site will load through Caddy and then refuse every request.
Two more lines before the first start. SIGNUPS_ENABLED is read once, when the API starts for the first time, and is not reapplied on restarts. A non-empty SIGNUP_ALLOWLIST is applied on every start and replaces whatever list is stored:
SIGNUPS_ENABLED=true
[email protected]
With the allowlist set and no SMTP configured, your own account can register without email verification, and nobody else can register at all. That is the sequence the docs recommend for a public host: allowlist first, then expose, then sign up. If you want password recovery later, fill SMTP_URL and EMAIL_FROM as well; Rakazo rejects SMTP settings that disable TLS, so use smtps:// on port 465 or smtp:// with STARTTLS.
Optionally add OPENROUTER_API_KEY now. Any other provider can be connected from the web UI after signup, so an empty key is fine. OK, next!
Step 3: Start Rakazo and check the containers
Run the installer again without the flag. It keeps your edited .env, validates that the five secrets are non-empty (an empty one fails the Compose config on purpose), pulls the images and brings the stack up:
bash install-images.sh
The first pull takes a while because of the computer image. When it finishes you should see the stack healthy:
docker compose --env-file .env -f docker-compose.images.yml ps
curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1:5173/
The computer and data-init entries show as exited; they only pull the desktop image and fix volume ownership. Postgres, the supervisor, the API, the worker and web should all be running. Web is bound to 127.0.0.1:5173 and the API to 127.0.0.1:3100, so nothing is reachable from the internet yet, and the Postgres port is not published at all. Keep it that way; the next step adds the one public door.
Step 4: Put HTTPS in front with Caddy
The web container proxies /api and /rpc to the API itself, so a reverse proxy only needs to reach port 5173. Caddy fetches and renews the certificate on its own. Install it from the Caddy project's apt repository:
sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | sudo tee /etc/apt/sources.list.d/caddy-stable.list
sudo chmod o+r /usr/share/keyrings/caddy-stable-archive-keyring.gpg /etc/apt/sources.list.d/caddy-stable.list
sudo apt update && sudo apt install -y caddy
Replace /etc/caddy/Caddyfile with the two-line site block from Rakazo's docs, then reload:
rakazo.example.com {
reverse_proxy 127.0.0.1:5173
}
sudo systemctl reload caddy
If the server runs ufw, allow SSH, HTTP and HTTPS and nothing else. Port 80 stays open because Caddy answers the certificate challenge there and redirects everything else to HTTPS:
sudo ufw allow OpenSSH
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable
Open https://rakazo.example.com. The sign-in page should load with a valid certificate. If you get a "Blocked request" error instead, the usual cause is RAKAZO_HOST still set to localhost; fix it and recreate the web container with docker compose --env-file .env -f docker-compose.images.yml up -d web.
Step 5: Create the owner account and connect a model
Register with the email from your allowlist. The first registered user becomes the deployment owner, which is why the allowlist went in before the first start. Then connect a model under the bot setup: an OpenRouter key if you set one in .env, otherwise a provider key or a ChatGPT or Claude subscription sign-in, which Rakazo supports through its Pi harness. Create a bot, open Agent computer and you should see a live Linux desktop streaming from the Docker computer on your server.
Local models work too, and they are the reason some people pick Rakazo over a hosted agent in the first place. Install Ollama on the same server or a GPU server next to it, following the Ollama install guide for Ubuntu, and add it as an OpenAI-compatible connection. Two details matter: from inside the containers 127.0.0.1 is the container, so Ollama must listen on an address the API container can reach, such as the Docker bridge address of the host; and bots with screen tools need a model that accepts images, so tick the connection's Supports images setting for a vision-capable model (centrally managed endpoints list theirs in RAKAZO_OPENAI_COMPATIBLE_VISION_MODELS instead). A text-only model still runs a bot. It just cannot read screenshots of what the computer is doing.
Which model to pick depends on what the bots do all day. A reader on r/GrokBot reported running six Rakazo agents on DeepSeek for a few dollars a day, and the bills others quote for hosted agents go the other way. Spend it where the work is: one capable model for the bot that writes to people, a cheap one for the bot that only triages.