Run OpenCode as a persistent background service, accessible from any device via Tailscale.
- Access from anywhere — Start a task from your phone, check results from your laptop
- Sessions persist — Close the browser, come back later, your session is still there
- Multiple clients — Terminal TUI and browser can connect to the same session simultaneously
- Survives crashes — systemd restarts the server automatically
- Linux with systemd (always-on laptop, home server, VPS, etc.)
- OpenCode installed and authenticated
- Tailscale connected to your tailnet
Phone (via Tailscale) ───────────┐
│
Laptop terminal (opencode attach) ├──▶ opencode web (:4096) ──▶ LLM
│ (systemd)
Browser (local network) ─────────┘
Resource usage: ~330MB RAM idle, negligible CPU.
| Task | Command |
|---|---|
| Attach from terminal | opencode attach http://localhost:4096 |
| Access remotely | http://[machine].[tailnet].ts.net:4096 |
| Check service status | systemctl --user status opencode-web |
| View logs | journalctl --user -u opencode-web -f |
| Restart service | systemctl --user restart opencode-web |
| Stop service | systemctl --user stop opencode-web |
which opencodemkdir -p ~/.config/systemd/userCreate ~/.config/systemd/user/opencode-web.service:
[Unit]
Description=OpenCode Web Server
After=network.target
[Service]
Type=simple
WorkingDirectory=%h
ExecStart=/path/to/opencode web --port 4096 --hostname 0.0.0.0
Restart=on-failure
RestartSec=5
[Install]
WantedBy=default.targetReplace /path/to/opencode with the output from step 1.
Note:
WorkingDirectory=%hstarts in your home directory. Change this to a specific project path if needed.
Port: 4096 is arbitrary — use any available port.
loginctl enable-linger $USERsystemctl --user daemon-reload
systemctl --user enable opencode-web
systemctl --user start opencode-webcurl http://localhost:4096/global/healthShould return: {"healthy":true,"version":"x.x.x"}
opencode attach http://localhost:4096Full TUI experience, same session as web. Detach with Ctrl+C.
Open http://[your-tailscale-hostname]:4096
| Command | Web UI | Use case |
|---|---|---|
opencode serve |
API only | Terminal-first via attach |
opencode web |
Full browser UI | Phone/tablet friendly |
Use web if you want browser access.
After upgrading OpenCode, restart the service to use the new version:
systemctl --user restart opencode-webThe running service continues using the old binary until restarted.
Wrong executable path:
which opencode
journalctl --user -u opencode-web -n 20lsof -i :4096
pkill -f "opencode web"
systemctl --user restart opencode-web- Verify Tailscale is running:
tailscale status - Check firewall isn't blocking the port
- Verify
--hostname 0.0.0.0is set
Add to the [Service] section:
Environment=OPENCODE_SERVER_USERNAME=your-username
Environment=OPENCODE_SERVER_PASSWORD=your-passwordUsername defaults to opencode if not set.
With Tailscale, usually unnecessary — your tailnet is already private.