Configure the remote CI runner¶
Guide to using the script that provisions a remote machine to run gitrust CI/CD pipelines via SSH + rsync + Dagger.
1. Role of the script¶
deployment/setup-remote-ci.sh (available in the gitrust source repository) prepares a remote build server used by the gitrust CI worker. It automates 6 steps:
- Checks SSH connectivity to the runner
- Install Docker (
curl https://get.docker.com | sh) if it is absent - Installs Dagger CLI (
curl https://dl.dagger.io/dagger/install.sh | sh) if absent - Creates the remote working directory (
CI_REMOTE_PATH) - Synchronize the
deployment/ci-engine/module (CI Easy mode) viarsync -az --delete - Smoke test: displays
docker --versionanddagger version
The script is idempotent: replays without damage, skips what already exists.
2. When to use it¶
| Scénario | Besoin de setup-remote-ci.sh ? |
|---|---|
Dev local, CI sur la même machine que gitrust (CI_REMOTE_HOST=localhost) |
Non — Docker et Dagger déjà installés localement, le ci-engine/ est lu depuis deployment/ci-engine/ directement |
| Prod on-premise, un seul serveur (gitrust + CI colocalisés) | Non — idem, un simple curl get.docker.com \| sh suffit sur le serveur gitrust |
| Prod avec runner CI dédié (machine séparée) | OUI — c'est le cas cible de ce script |
| Prod avec plusieurs instances gitrust partageant un runner | OUI — le runner est provisionné une fois, chaque instance pointe dessus |
| CI dans Kubernetes / Nomad / runner managé cloud | Non — ce script est pensé pour une VM Debian/Ubuntu classique |
3. Target architecture¶
+---------------------+ SSH + rsync +-----------------------+
| Gitrust (web+SSH) |---------------------------| CI Runner (distant) |
| <your-server-ip> | CI_REMOTE_HOST/USER/KEY | <ci-runner-ip> |
| | | |
| Worker CI Tokio | push .gitrust-ci.yml | /opt/gitrust-ci/ |
| (dans gitrust.bin) | + code source checkout | ├── ci-engine/ |
| | | └── workspaces/ |
| | dagger call ... | |
| | | Docker + Dagger CLI |
+---------------------+ +-----------------------+
The gitrust CI worker (Tokio task in the main binary) does not run Docker locally — it delegates to the runner via SSH. This makes it possible to isolate greedy CPU/RAM workloads (Rust, Node, Docker builds) from the gitrust web process.
4. Prerequisites¶
On the execution machine (where we launch the script)¶
| Outil | Rôle |
|---|---|
bash ≥ 4 |
Interpréteur |
ssh, rsync |
Transport et synchro |
ssh-agent chargé ou CI_REMOTE_SSH_KEY défini |
Auth SSH sans mot de passe interactif (BatchMode=yes) |
Fichier .env avec les variables CI_REMOTE_* |
Config (voir section 5) |
On the distant runner¶
| Pré-requis | Pourquoi |
|---|---|
| OS Linux récent (Debian 12+, Ubuntu 22.04+) | Docker install script compatible |
User avec sudo passwordless ou droits docker |
get.docker.com fait sudo en interne |
Clé SSH publique du user local dans ~/.ssh/authorized_keys |
Auth SSH non-interactive |
Réseau sortant autorisé vers get.docker.com et dl.dagger.io |
Installation des binaires |
Au minimum ~5 Go libres dans CI_REMOTE_PATH |
Images Docker + workspaces |
5. Expected environment variables (the .env)¶
The script sources a .env file whose path is passed as an argument, or by default ../.env (relative to the script, therefore <project_root>/.env).
Table of variables¶
| Variable | Obligatoire | Défaut | Description |
|---|---|---|---|
CI_REMOTE_HOST |
OUI | — | Hostname ou IP du runner (ex: ci-runner.internal) |
CI_REMOTE_USER |
non | $(whoami) (user courant) |
Compte SSH sur le runner |
CI_REMOTE_SSH_PORT |
non | 22 |
Port SSH du runner |
CI_REMOTE_PATH |
non | /opt/gitrust-ci |
Répertoire de travail distant (créé par le script) |
CI_REMOTE_SSH_KEY |
non | — (ssh-agent) | Chemin d'une clé privée SSH spécifique |
These variables are the same as those read by gitrust at runtime. Centralizing in .env avoids discrepancies between provisioning and runtime.
Minimal example of .env (dedicated runner)¶
# --- CI runner distant ---
CI_REMOTE_HOST=<ci-runner-ip>
CI_REMOTE_USER=ci-runner
CI_REMOTE_SSH_PORT=22
CI_REMOTE_PATH=/opt/gitrust-ci
CI_REMOTE_SSH_KEY=/home/gitrust/.ssh/ci_runner_ed25519
Complete .env example coexisting with gitrust config¶
DATABASE_URL=postgres://...
JWT_SECRET=...
# ... (voir .env.example pour le reste)
# --- CI/CD (lu par gitrust ET par setup-remote-ci.sh) ---
CI_ENABLED=true
CI_MAX_CONCURRENT=4
CI_REMOTE_HOST=ci-runner.internal
CI_REMOTE_USER=ci-runner
CI_REMOTE_PATH=/opt/gitrust-ci
CI_REMOTE_SSH_KEY=/opt/gitrust/data/ci_runner_key
“Local runner” case (no need for the script)¶
In this case, install Docker and Dagger directly on the gitrust machine, copy deployment/ci-engine/ to CI_ENGINE_PATH (default /opt/gitrust/ci-engine) and move on. Do not run setup-remote-ci.sh with CI_REMOTE_HOST=localhost.
6. Use¶
From the project root (default)¶
With an explicit .env¶
./deployment/setup-remote-ci.sh /chemin/vers/mon.env
# Utile si vous avez .env.production, .env.staging, etc.
./deployment/setup-remote-ci.sh .env.production
Expected output¶
==> Configuration :
Serveur : ci-runner@<ci-runner-ip>
Port SSH : 22
Chemin : /opt/gitrust-ci
==> [1/6] Vérification de la connectivité SSH...
SSH OK
OK
==> [2/6] Vérification de Docker...
Docker déjà installé
==> [3/6] Vérification de Dagger CLI...
Installation de Dagger CLI...
Dagger installé
==> [4/6] Création du répertoire distant...
/opt/gitrust-ci créé
==> [5/6] Synchronisation du ci-engine...
sending incremental file list
ci-engine/
ci-engine/profiles/
...
ci-engine synchronisé vers /opt/gitrust-ci/ci-engine/
==> [6/6] Smoke test...
Versions distantes :
Docker version 28.0.1, build abcd123
dagger v0.19.2 (linux/amd64)
==> Setup terminé. Le serveur ci-runner@<ci-runner-ip> est prêt pour l'exécution CI.
Pensez à configurer CI_EXECUTION_MODE=remote dans votre .env
7. What is found on the runner¶
After successful execution:
/opt/gitrust-ci/ <- CI_REMOTE_PATH
└── ci-engine/ <- synchronisé depuis deployment/ci-engine/
├── README.md
└── profiles/ <- templates par stack (Python, Node, Rust, ...)
Plus, installés globalement :
- /usr/bin/docker (or equivalent) + socket /var/run/docker.sock
- /usr/local/bin/dagger
Pipeline workspaces (temporary checkouts) are created by the gitrust CI worker at runtime under CI_WORKSPACE_PATH (default /tmp/gitrust-ci) — not by this script.
8. Post-setup checks¶
From the gitrust machine¶
# Source le .env
set -a; source .env.production; set +a
# 1. SSH direct (doit passer sans prompt)
ssh -p ${CI_REMOTE_SSH_PORT:-22} \
${CI_REMOTE_SSH_KEY:+-i $CI_REMOTE_SSH_KEY} \
$CI_REMOTE_USER@$CI_REMOTE_HOST 'docker info && dagger version'
# 2. Rsync round-trip (lecture/écriture sur CI_REMOTE_PATH)
echo "test" | ssh $CI_REMOTE_USER@$CI_REMOTE_HOST \
"cat > $CI_REMOTE_PATH/.gitrust-test && cat $CI_REMOTE_PATH/.gitrust-test && rm $CI_REMOTE_PATH/.gitrust-test"
# Attendu : "test"
Test pipeline via gitrust¶
Pushing a repository with a minimal .gitrust-ci.yml:
Then in the gitrust UI: Pipelines tab → the pipeline must go to success status.
9. Update after modification of ci-engine/¶
The synchronized ci-engine/ is not “live-linked”: replay the script after modification on the source side.
# Modifier deployment/ci-engine/profiles/*.py ou similaire
./deployment/setup-remote-ci.sh
# Les étapes 2-3 sont skip (déjà installés), seule l'étape 5 refait le rsync
rsync -az --delete deletes runner-side files that no longer exist locally — ensures consistency.
10. Troubleshooting¶
| Symptôme | Cause probable | Fix |
|---|---|---|
ERREUR: fichier .env introuvable |
.env absent ou chemin incorrect |
cp .env.example .env && $EDITOR .env ou passer le chemin : ./setup-remote-ci.sh /path/to/.env |
ERREUR: CI_REMOTE_HOST non défini |
Variable commentée ou absente | Décommenter CI_REMOTE_HOST=... dans le .env |
ERREUR: impossible de se connecter |
SSH bloqué, mauvais user, clé non autorisée | Tester manuellement : ssh -v -p X user@host ; vérifier ~/.ssh/authorized_keys sur le runner |
Permission denied (publickey) |
BatchMode=yes interdit les prompts, clé non chargée |
ssh-add ~/.ssh/ci_runner_ed25519 ou définir CI_REMOTE_SSH_KEY=/path/to/key |
sudo: a password is required pendant l'install Docker |
User sans NOPASSWD sudo |
Ajouter le user au sudoers : ci-runner ALL=(ALL) NOPASSWD:ALL (runner uniquement) |
curl: (7) Failed to connect to get.docker.com |
Réseau sortant du runner bloqué | Whitelist get.docker.com et dl.dagger.io, ou pré-installer Docker + Dagger manuellement |
ATTENTION: ... ci-engine introuvable |
Lancé hors du repo gitrust | cd dans la racine du projet avant de lancer |
Pipeline reste queued indéfiniment |
Worker CI ne trouve pas le runner | Vérifier CI_EXECUTION_MODE=remote dans .env gitrust + logs : journalctl -u gitrust \| grep -i 'ci\|dagger' |
dagger: command not found au smoke test |
$PATH du user SSH ne contient pas /usr/local/bin |
echo 'export PATH=$PATH:/usr/local/bin' >> ~/.bashrc sur le runner, ou CI_DAGGER_BIN=/usr/local/bin/dagger côté gitrust |
11. Security¶
- The CI runner executes arbitrary code coming from hosted repositories. Never collocate it with sensitive secrets (prod PG, prod keys).
- Isolate network: block outgoing access from the runner to the private LAN (only Internet access for
docker pullis necessary). - Limit
sudo NOPASSWDto the strict minimum on the runner (ideally: just for thedockerandaptcommands). - Regular rotation of the SSH key
CI_REMOTE_SSH_KEY. Revoke it in~/.ssh/authorized_keyson the runner side in case of suspicion. - The runner must not be able to connect via SSH to the gitrust machine (unidirectional).