Set up a server from scratch
This page covers three starting points — jump to yours:
- From zero — no server at all. Rent a small Linux server, run one install command, come back with a box address and box code.
- Agents — your box is connected but the scan found no coding agent. Install one.
- Models — connect a model: a subscription login or an API key, on your box, on your terms.
From zero
Section titled “From zero”You’ll rent a small Linux server, run one install command, add a model key, and come back to the Gray app holding the box address and one-time box code the installer prints. Budget about fifteen minutes. Already have a machine — a VPS, a homelab, a spare laptop? Skip the first step and go straight to Set up your box.
1. Pick a server
Section titled “1. Pick a server”Gray’s needs are modest: Linux (Ubuntu 24.04 or Debian 12), 2 GB RAM, Python 3.11+, systemd — the smallest tier at most providers, around $5/month. Full list: Compatibility.
Two providers we test against (any equivalent works):
- Hetzner Cloud — create a server: image Ubuntu 24.04, type CPX11 (2 GB), add your SSH key, create. Our release tests run on exactly this tier.
- DigitalOcean — create a Basic Droplet: Ubuntu 24.04, the 2 GB option, add your SSH key, create.
Either way you end with an IP address. Connect to it:
ssh root@YOUR_SERVER_IP2. Install Gray — one command
Section titled “2. Install Gray — one command”On the server:
curl -fsSL https://get.layergray.com | sudo shThe installer handles everything: dependencies, a Python virtualenv, a
0600-tight env file at /etc/gray/server.env, Tailscale when it’s missing,
and the gray-server systemd service. It then waits for the server to come
up and ends on a PAIR YOUR PHONE card with the two things the app asks
for:
- Box address — an
https://…ts.net:8443URL on your tailnet, and - your one-time box code — the proof that you own this box. (The server’s own log calls it a claim code — same thing.)
Keep both on screen; you’ll paste them into the app in step 6.
If the run is interrupted, just run the same command again — the installer is safe to re-run and repairs a half-finished install.
3. Tailscale — how your phone reaches the box
Section titled “3. Tailscale — how your phone reaches the box”Your box and your phone must be on the same tailnet — the app talks to
the box address from step 2, which only exists inside it. The installer
installs Tailscale and publishes the server for you (HTTPS on port 8443);
the only part it can’t do is sign the box in. If the installer says Tailscale
isn’t up yet:
sudo tailscale up…then re-run the install one-liner (it’s safe) so it finishes the publish step, or run the exact command the installer printed:
sudo tailscale serve --yes --bg --https=8443 http://127.0.0.1:8848Enable MagicDNS and HTTPS certificates in the Tailscale admin console (Settings → DNS), and install the Tailscale app on your phone, signed in to the same tailnet. Why it works this way — no public ports, real certificates: Tailscale.
4. Bring your own keys
Section titled “4. Bring your own keys”Add a model credential next — see Models below. The short
version: an Anthropic key (or Claude subscription login) powers the brain;
an OpenAI key optionally adds premium realtime voice. Keys go in
/etc/gray/server.env and stay on your hardware.
5. If something goes wrong
Section titled “5. If something goes wrong”- No box code in the installer output — read it from the box’s journal
(the log line says “claim code” — that’s the box code):
journalctl -u gray-server | grep -i 'claim code'. More: Box code. - The box is already claimed (“This box is already claimed…”) — sign in as the account that set it up, or have that owner release it (Me → Account → delete account on the box) and sign in fresh — the box prints a new box code. Full story: Box code.
- A sign-in code never arrives — a box with no email sender prints the
6-digit code to its journal instead. The app shows this exact command,
tap-to-copy:
journalctl -u gray-server | grep -i 'sign-in code'. See Sign in. - “Checking box address…” never succeeds — the phone can’t reach the box. Check the Tailscale app on your phone is connected to the same tailnet, and MagicDNS + HTTPS certificates are enabled. More: Connection problems.
- The install died midway — run the one-liner again; it self-repairs.
- The server won’t come up —
systemctl status gray-serverandjournalctl -u gray-server -eshow why. If the installer said port8848is busy, something else owns it — re-run with--port.
6. Back in the app
Section titled “6. Back in the app”Open Gray. You create your account first — email, then the 6-digit code — and land on Connect your box. The screen shows the same install command with a copy button; you’ve already run it, so paste the box address and box code from step 2 and tap Connect box. Gray checks the address, claims the box, walks you through a first win and a notifications ask, then scans the box for agents and models. When you see Scan complete, you’re done — say your first command: Your first voice command.
(Adding a box to an already-running Gray later is the same idea from Servers → Add a server, which can also auto-detect on the tailnet or take a pairing code — see Servers.)
Agents
Section titled “Agents”Your box is connected but the scan came back empty — no coding agent
installed. Gray works through agents that live on the box; it detects these
seven on the box’s PATH:
Claude Code · Codex · Grok · Gemini · Aider · OpenCode · Cursor
Install at least one. Claude Code is the one Gray’s brain runs on, so start there — on the box:
npm install -g @anthropic-ai/claude-code(Node 18+ — apt install nodejs npm on a stock box gets you there. Each of
the other agents installs per its own guide; Gray finds whatever appears on
the PATH.)
Then rescan from the app: Me → Brain → Stack → Rescan. Installed agents show up under Agents, and their local sessions appear in Activity. Next: connect a model — Models.
Models
Section titled “Models”An agent needs a model behind it. There are exactly two ways to connect one, and both keep the same boundary: credential acquisition happens on the provider’s own site or console — Gray never captures or automates it, and keys stay on the box. There is no API-key field anywhere in the Gray app.
Path 1 — subscription login. Run the agent’s own login on the box; it hands you to the provider’s own browser sign-in, and the session token lands on the box through the agent’s own flow. Gray never sees it. For the brain:
claude # follow the sign-in prompt — uses your Claude subscriptionPath 2 — API key (BYOK). Create the key in the provider’s own console, paste it into the box’s env file, restart. It stays on the box:
nano /etc/gray/server.env# ANTHROPIC_API_KEY=sk-ant-...# OPENAI_API_KEY=sk-... (optional — premium realtime voice)
systemctl restart gray-serverWhat each credential powers, and where to create it:
| Provider | What Gray uses it for | Get it | Billing |
|---|---|---|---|
| Claude (Anthropic) | The brain — conversation, agents, summaries. Subscription login or ANTHROPIC_API_KEY | claude.ai (subscription) · console.anthropic.com (API key) | Flat monthly (subscription) · usage-based, prepaid credits (API) |
| OpenAI | Premium realtime voice + transcription — optional; on-device voice works without it | platform.openai.com | Usage-based, prepaid credits |
| Grok / xAI | The Grok agent CLI, when you use it | console.x.ai | Usage-based |
| Gemini (Google) | The Gemini agent CLI, when you use it | aistudio.google.com | Usage-based, free tier available |
The brain itself runs on Claude — the other logins power their own agent CLIs on the box. After adding a credential, rescan (Me → Brain → Stack → Rescan): Gray reports presence only — a detected login or a masked last-4 fingerprint of an env key — never the secret itself. Where keys live and why: Key storage · full detection list: Models & keys · enterprise routing (Bedrock, Vertex, proxies): Configuration reference.