opencode · VPS
// auto-hébergé · privé · accessible partoutself-hosted · private · reachable anywhere

opencode sur ton VPS,
derrière un tunnel SSH.
opencode on your VPS,
behind an SSH tunnel.

Installe l'agent de codage IA open source opencode sur un petit VPS et accèdes-y uniquement depuis tes appareils — jamais exposé sur l'internet public. Install the open-source AI coding agent opencode on a small VPS and reach it only from your own devices — never exposed to the public internet.

0 port applicatif publicpublic app port 1 vCPU · 2 Go RAM tunnel SSH seulSSH tunnel only Ubuntu 24.04
make all — bootstrap
$ make all VPS_HOST=203.0.113.7
==> ufw : seul le port 22 (SSH) est ouvert==> ufw: only port 22 (SSH) is open
==> SSH : auth par mot de passe désactivée==> SSH: password auth disabled
==> swap 2G · mises à jour de sécurité auto==> swap 2G · automatic security updates
==> opencode web : 127.0.0.1 uniquement==> opencode web: 127.0.0.1 only
==> test : tout est OK==> test: all OK
==> Terminé. make tunnel pour te connecter.==> Done. make tunnel to connect.
$  
01

Pourquoi ce setup plutôt qu'un autre ?Why this setup rather than another?

Un seul mécanisme d'accès, aucune surface publique, et tout est scripté et vérifiable. One access mechanism, no public surface, and everything is scripted and verifiable.

Pas d'exposition publiqueNo public exposure

Aucun port HTTP/HTTPS ouvert sur l'internet. L'accès passe exclusivement par un tunnel SSH, avec une authentification applicative en plus.No HTTP/HTTPS port open to the internet. Access goes exclusively through an SSH tunnel, with app-level authentication on top.

LégerLightweight

Un VPS 1 vCPU / 2 Go de RAM suffit — opencode ne fait que des appels API sortants, il ne fait tourner aucun modèle en local (pas de GPU).A 1 vCPU / 2 GB RAM VPS is enough — opencode only makes outbound API calls; it runs no model locally (no GPU).

SimpleSimple

Un seul mécanisme d'accès (SSH), pas de daemon tiers, pas de compte externe à gérer.A single access mechanism (SSH), no third-party daemon, no external account to manage.

ReproductibleReproducible

Tout est scripté (un Makefile + un script de vérification), sans configuration manuelle non documentée.Everything is scripted (a Makefile + a verification script), with no undocumented manual configuration.

VérifiableVerifiable

Une suite de tests (make test) contrôle l'état réel du serveur, y compris les points de sécurité les plus faciles à mal configurer.A test suite (make test) checks the server's actual state, including the security points easiest to misconfigure.

02

Architecture

Le VPS n'a aucun port applicatif ouvert sur l'internet — seul SSH (22) l'est. Tout le trafic vers opencode transite par le tunnel, chiffré de bout en bout. The VPS has no application port open to the internet — only SSH (22) is. All traffic to opencode goes through the tunnel, encrypted end to end.

03

PrérequisRequirements

  • Un VPS Ubuntu 24.04 avec accès root/sudo et ta clé SSH déjà installée dessus.An Ubuntu 24.04 VPS with root/sudo access and your SSH key already installed on it.
  • make, ssh et scp disponibles en local (déjà présents sur macOS/Linux).make, ssh and scp available locally (already present on macOS/Linux).

Testé chez Gandi, mais rien n'y est spécifique. N'importe quel hébergeur convient : Hetzner Cloud, DigitalOcean, Vultr, Scaleway, OVHcloud, Linode/Akamai… Seule l'étape gandi-firewall porte le nom de Gandi — elle ne fait que rappeler de fermer les ports publics dans le pare-feu cloud de ta console (tous les hébergeurs en proposent un). Tested on Gandi, but nothing is host-specific. Any provider works: Hetzner Cloud, DigitalOcean, Vultr, Scaleway, OVHcloud, Linode/Akamai… Only the gandi-firewall step is named after Gandi — it merely reminds you to close public ports in your console's cloud firewall (every provider has one).

04

Installation

Le pipeline s'arrête à chaque étape qui demande une action de ta part et affiche exactement quoi faire. Compte 10–15 minutes. The pipeline pauses at every step that needs an action from you and shows exactly what to do. Budget 10–15 minutes.

  1. Cloner le dépôtClone the repository

    Récupère le Makefile et les scripts en local.

    Grab the Makefile and scripts locally.

    bash
    $ git clone https://github.com/StevenMorlier/open_router_vps.git
    $ cd open_router_vps
  2. Tout dérouler avec make allRun it all with make all

    Une seule commande enchaîne durcissement, swap, mises à jour, installation d'opencode et vérifications, avec des pauses de validation.

    A single command chains hardening, swap, updates, opencode install and checks, with confirmation pauses.

    bash
    $ make all VPS_HOST=<ip-du-vps>

    Chaque étape est aussi utilisable seule (make harden VPS_HOST=…, etc.). Voici ce que make all enchaîne :Each step can also run on its own (make harden VPS_HOST=…, etc.). Here is what make all chains:

    ÉtapeStep Ce qu'elle faitWhat it does
    gandi-firewallRappel des réglages à faire dans la console de ton hébergeur (pare-feu cloud).Reminder of the settings to make in your provider's console (cloud firewall).
    hardenufw : SSH autorisé, tout le reste bloqué + désactivation de l'auth SSH par mot de passe (clé uniquement).ufw: SSH allowed, everything else blocked + SSH password auth disabled (key only).
    swapAjoute un fichier de swap (filet de sécurité sur un VPS à faible RAM).Adds a swap file (safety net on a low-RAM VPS).
    security-updatesActive les mises à jour de sécurité auto + demande si le reboot auto doit être activé.Enables automatic security updates + asks whether auto-reboot should be on.
    opencode-installInstalle opencode via le script officiel.Installs opencode via the official script.
    opencode-web-serviceDéploie opencode web en service systemd, lié à 127.0.0.1 uniquement, mot de passe généré.Deploys opencode web as a systemd service, bound to 127.0.0.1 only, generated password.
    testSuite de vérifications de bout en bout.End-to-end verification suite.

    Note le mot de passeWrite down the password

    L'étape opencode-web-service génère un mot de passe applicatif affiché une seule fois. Note-le : l'identifiant à saisir dans le navigateur est opencode.The opencode-web-service step generates an app password shown only once. Write it down: the username to enter in the browser is opencode.

  3. Se connecter via le tunnelConnect through the tunnel

    Cette commande reste au premier plan (Ctrl+C pour fermer) et ouvre le tunnel SSH.

    This command stays in the foreground (Ctrl+C to close) and opens the SSH tunnel.

    bash
    $ make tunnel VPS_HOST=<ip-du-vps>

    Une fois connecté, ouvre http://127.0.0.1:4096 dans ton navigateur.

    Once connected, open http://127.0.0.1:4096 in your browser.

    http://127.0.0.1:4096
    Interface web d'opencode ouverte via le tunnel SSH sur 127.0.0.1:4096
    opencode web, accédé en local via le tunnel SSH.opencode web, reached locally through the SSH tunnel.
05

Usage quotidienDaily use

Au quotidien, une seule commande suffit. Ouvre le tunnel, garde le terminal ouvert pendant la session, puis rends-toi sur http://127.0.0.1:4096.Day to day, one command is enough. Open the tunnel, keep the terminal open during your session, then head to http://127.0.0.1:4096.

bash — à garder ouvertkeep open
$ make tunnel VPS_HOST=<ip-du-vps>
==> http://127.0.0.1:4096 une fois connecté (Ctrl+C pour fermer)

Mettre le Mac en veille coupe la connexion ; il suffit de relancer make tunnel. Ce n'est pas un accès « toujours connecté » en arrière-plan.Putting the Mac to sleep drops the connection; just re-run make tunnel. This is not an always-on background access.

06

Accès depuis iPhone (Termius)Access from iPhone (Termius)

Le tunnel n'est pas limité au Mac : tout client SSH mobile supportant le « local port forwarding » fonctionne, par exemple Termius. Dans Termius : VaultsPort Forwarding+, avec Local Port / Destination port = 4096 et Destination address = 127.0.0.1.The tunnel isn't limited to a Mac: any mobile SSH client supporting local port forwarding works, e.g. Termius. In Termius: VaultsPort Forwarding+, with Local Port / Destination port = 4096 and Destination address = 127.0.0.1.

Termius — section Port Forwarding du vault
La section Port ForwardingThe Port Forwarding section
Termius — édition de la règle de port forwarding
Éditer la règleEdit the rule
Termius — règle de port forwarding active
Règle activeRule active

Déconnexions fréquentes sur iPhoneFrequent drops on iPhone

Mise en veille de l'app, bascule Wi-Fi/5G ou passage en arrière-plan coupent la règle. Il faut la relancer manuellement : c'est un dépannage ponctuel, pas un accès « toujours connecté » fiable.App sleep, a Wi-Fi/5G switch or backgrounding cut the rule. You must restart it manually: it's an occasional workaround, not a reliable always-on access.

07

Synchroniser du code (GitHub)Sync your code (GitHub)

Génère une clé SSH dédiée à un repo sur le VPS et affiche la clé publique à ajouter en Deploy Key sur ce repo précis.Generate an SSH key dedicated to one repo on the VPS and print the public key to add as a Deploy Key on that specific repo.

bash
$ make github-deploy-key VPS_HOST=<ip> REPO=mon-repo

GitHub interdit qu'une même clé serve sur plusieurs repos (key already in use) — c'est volontaire, ça limite le rayon d'action de chaque clé. Pour un autre repo, relance avec un REPO= différent. Le VPS n'a alors accès qu'aux repos où une clé a été explicitement ajoutée, jamais à tout un compte.GitHub forbids reusing one key across repos (key already in use) — intentional, it limits each key's blast radius. For another repo, re-run with a different REPO=. The VPS then only has access to the repos where a key was explicitly added, never to a whole account.

08

Sécurité — modèle de menaceSecurity — threat model

Les risques envisagés et le choix fait pour chacun.The risks considered and the choice made for each.

RisqueRisk MitigationMitigation
réseaunetwork Scan / brute-force depuis internetInternet scan / brute-force Aucun port applicatif public — uniquement SSH (clé, pas de mot de passe).No public app port — only SSH (key, no password).
SSH Vol de la clé privée SSHSSH private key theft harden force la clé uniquement ; mets une passphrase sur ta clé ; fail2ban en option si le VPS reste exposé longtemps.harden enforces key-only; put a passphrase on your key; fail2ban optionally if the VPS stays exposed long.
tiersthird party Accès à l'UI opencode par un tiers ayant un accès SSHA third party with SSH access reaching the opencode UI Mot de passe applicatif dédié, indépendant du tunnel.Dedicated app password, independent of the tunnel.
GitHub Serveur compromis → accès au compte GitHubCompromised server → GitHub account access Deploy keys par repo, jamais un token donnant accès à tout le compte.Deploy keys per repo, never a token granting whole-account access.
systèmesystem Paquets système vulnérablesVulnerable system packages Mises à jour de sécurité automatiques ; reboot programmé au choix.Automatic security updates; scheduled reboot optional.

Vérifie tout ça d'un coup avec make test VPS_HOST=<ip> : sortie OK / FAIL ligne par ligne, code de sortie non-nul si un problème est détecté.Check it all at once with make test VPS_HOST=<ip>: line-by-line OK / FAIL output, non-zero exit code if a problem is found.

09

Piège à connaîtreA gotcha to know

Sélecteur de projet cassé dans l'UI web (opencode v1.18.4)Broken project picker in the web UI (opencode v1.18.4)

Dans le dialogue « Open project », chercher un dossier par son nom renvoie toujours « No folders found », même s'il existe. Solution : tape le chemin absolu complet (ex : /home/ubuntu/projects/mon-projet) — ça déclenche une complétion de chemin qui fonctionne. Une fois ouvert, le projet reste enregistré normalement.In the "Open project" dialog, searching a folder by name always returns "No folders found", even when it exists. Fix: type the full absolute path (e.g. /home/ubuntu/projects/my-project) — this triggers a path completion that works. Once opened, the project is remembered normally.

Bugs opencode : #8325, #9795, #7111.opencode issues: #8325, #9795, #7111.

10

FAQ / DépannageTroubleshooting

Combien ça coûte ? (exemple)How much does it cost? (example)

Ce projet ne couvre que la partie VPS — le prix du modèle IA relève d'opencode et de ton fournisseur d'API, pas de ce setup, et tu peux brancher opencode sur l'offre de ton choix. À titre indicatif seulement, la configuration personnelle de l'auteur : VPS Gandi ~8 €/mois (1 vCPU / 2 Go de RAM) et un abonnement « opencode Go » à ~5 €/mois avec le modèle GLM 5.2. Ce sont des choix personnels donnés en exemple, pas un minimum requis.This project only covers the VPS side — the AI model's price is opencode's and your API provider's domain, not this setup, and you can point opencode at whatever plan you like. As a rough example only, the author's personal setup: a Gandi VPS at ~€8/mo (1 vCPU / 2 GB RAM) and an "opencode Go" subscription at ~€5/mo with the GLM 5.2 model. These are personal choices given as an example, not a required minimum.

Le tunnel se coupe, ou « connection refused » sur 127.0.0.1:4096The tunnel drops, or "connection refused" on 127.0.0.1:4096

make tunnel doit rester ouvert au premier plan pendant toute la session. S'il s'est fermé (Mac en veille, changement de réseau), relance make tunnel VPS_HOST=<ip>. Le tunnel envoie déjà des keepalives ; si ça coupe quand même, c'est presque toujours la veille de la machine ou une bascule réseau, pas le VPS.make tunnel must stay open in the foreground for the whole session. If it closed (Mac asleep, network change), re-run make tunnel VPS_HOST=<ip>. The tunnel already sends keepalives; if it still drops, it's almost always machine sleep or a network switch, not the VPS.

« Address already in use » au lancement de make tunnel"Address already in use" when starting make tunnel

Le port local 4096 est déjà pris (souvent un ancien tunnel resté ouvert). Trouve le process avec lsof -iTCP:4096 -sTCP:LISTEN puis ferme-le, ou ouvre le tunnel sur un autre port local :Local port 4096 is already taken (often an old tunnel left open). Find the process with lsof -iTCP:4096 -sTCP:LISTEN and close it, or open the tunnel on another local port:

bash
$ ssh -N -L 5000:127.0.0.1:4096 ubuntu@<ip>   # puis http://127.0.0.1:5000

J'ai perdu (ou je veux changer) le mot de passe opencode-webI lost (or want to change) the opencode-web password

Le mot de passe n'est stocké nulle part en clair côté client : il n'est affiché qu'une fois à l'installation. Pour en régénérer un :The password is stored nowhere in clear text on the client: it is shown only once at install time. To regenerate one:

bash
$ ssh ubuntu@<ip>
$ NEW=$(openssl rand -base64 18); echo "$NEW"
$ printf 'OPENCODE_SERVER_PASSWORD=%s\n' "$NEW" | sudo install -m 600 /dev/stdin /etc/opencode-web.env
$ exit
$ make restart VPS_HOST=<ip>   # l'EnvironmentFile n'est relu qu'au redémarrage

L'identifiant à saisir dans le navigateur est opencode, le mot de passe est celui-ci.The username to enter in the browser is opencode, the password is this one.

Le service opencode-web ne démarre pasThe opencode-web service won't start

Regarde la cause : ssh ubuntu@<ip> 'systemctl status opencode-web.service; journalctl -u opencode-web.service -n 50 --no-pager'. Causes fréquentes : opencode-install pas passé (binaire absent), dossier /home/ubuntu/projects inexistant, ou /etc/opencode-web.env manquant. Après correction : make restart puis make test.Check the cause: ssh ubuntu@<ip> 'systemctl status opencode-web.service; journalctl -u opencode-web.service -n 50 --no-pager'. Common causes: opencode-install not run (missing binary), /home/ubuntu/projects folder absent, or /etc/opencode-web.env missing. After fixing: make restart then make test.

SSH : « Permission denied (publickey) »SSH: "Permission denied (publickey)"

harden désactive l'auth par mot de passe : seule ta clé est acceptée ensuite. Assure-toi que ta clé publique est bien dans ~/.ssh/authorized_keys sur le VPS avant de lancer harden, sinon tu risques de te verrouiller dehors (récupération via la console de secours KVM/VNC de l'hébergeur).harden disables password auth: only your key is accepted afterward. Make sure your public key is in ~/.ssh/authorized_keys on the VPS before running harden, or you risk locking yourself out (recover via the provider's KVM/VNC rescue console).