CI/CD Integration
Jiji is a single binary, installed the same way in CI as anywhere else:
curl -fsSL https://get.jiji.run/install.sh | shjiji deploy always asks for confirmation before doing anything - pass
-y/--yes in every non-interactive pipeline below, or it exits with an
error instead of hanging on a prompt nothing can answer.
GitHub Actions
Basic deployment
# .github/workflows/deploy.yml
name: Deploy
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install Jiji
run: |
curl -fsSL https://get.jiji.run/install.sh | sh
echo "$HOME/.local/bin" >> "$GITHUB_PATH"
- name: Setup SSH
run: |
mkdir -p ~/.ssh
echo "${{ secrets.SSH_PRIVATE_KEY }}" > ~/.ssh/id_ed25519
chmod 600 ~/.ssh/id_ed25519
ssh-keyscan -H ${{ secrets.SERVER_HOST }} >> ~/.ssh/known_hosts
- name: Deploy
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: jiji --host-env deploy --build -e production -y--host-env matters here: your secrets are real CI environment variables,
not a .env file, so jiji needs the fallback to pick them up.
With version tags
name: Deploy Tagged Release
on:
push:
tags:
- 'v*'
jobs:
deploy:
runs-on: ubuntu-latest
steps:
# ... install jiji as above ...
- name: Deploy
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
VERSION=${GITHUB_REF#refs/tags/}
jiji --host-env deploy --build --version "$VERSION" -e production -yStaging and production
name: Deploy
on:
push:
branches: [main, develop]
jobs:
deploy-staging:
if: github.ref == 'refs/heads/develop'
runs-on: ubuntu-latest
steps:
# ... install jiji ...
- name: Deploy to Staging
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: jiji --host-env deploy --build -e staging -y
deploy-production:
if: github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
steps:
# ... install jiji ...
- name: Deploy to Production
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: jiji --host-env deploy --build -e production -yGitLab CI
# .gitlab-ci.yml
stages:
- deploy
deploy:
stage: deploy
image: debian:stable-slim
before_script:
- apt-get update && apt-get install -y curl
- curl -fsSL https://get.jiji.run/install.sh | sh
- export PATH="$HOME/.local/bin:$PATH"
- mkdir -p ~/.ssh
- echo "$SSH_PRIVATE_KEY" > ~/.ssh/id_ed25519
- chmod 600 ~/.ssh/id_ed25519
script:
- jiji --host-env deploy --build -e production -y
only:
- main
variables:
GITHUB_TOKEN: $CI_REGISTRY_PASSWORDRequired Secrets
| Secret | Description |
|---|---|
SSH_PRIVATE_KEY | Private key for server access |
SERVER_HOST | Server hostname (for ssh-keyscan) |
GITHUB_TOKEN | Registry authentication (if using GHCR) |
SSH Key Setup
ssh-keygen -t ed25519 -C "deploy@ci" -f deploy_keyAdd the public key to each server, and the private key as a CI secret:
# On each server
echo "ssh-ed25519 AAAA... deploy@ci" >> ~/.ssh/authorized_keysBest Practices
Deployment locks
jiji deploy locks only the specific logical replicas it’s about to
touch (plus a shared proxy lock on any ingress host involved), not the
whole project - an unrelated offline host or a different replica never
blocks a targeted deploy. Locks are released on both the success and
failure path. A normal pipeline only needs the deploy step:
- name: Deploy
run: jiji deploy --build -yjiji lock acquire is a separate, whole-project maintenance lock used
by jiji network setup/backup/restore/recover/compact. It does
not block jiji deploy, service restart, or service rollback -
those use a different, per-replica lock scope entirely. Use jiji lock acquire to reserve a window around network-layer maintenance, not to gate
deploys; if a specific replica’s lock is stuck, clear it directly with
jiji lock release --replica <id> instead.
Tag every deployment
- name: Deploy
run: |
VERSION=$(git rev-parse --short HEAD)
jiji deploy --build --version "$VERSION" -yVerify after deploying
- name: Deploy
run: jiji deploy --build -y
- name: Verify
run: |
sleep 10
jiji service logs -S api --since 1m | grep -v ERRORNotifications
- name: Notify Success
if: success()
run: |
curl -X POST ${{ secrets.SLACK_WEBHOOK }} \
-d '{"text":"Deployed to production successfully"}'
- name: Notify Failure
if: failure()
run: |
curl -X POST ${{ secrets.SLACK_WEBHOOK }} \
-d '{"text":"Deployment failed!"}'Troubleshooting CI Deployments
SSH connection failed: check the key is formatted correctly in your CI
secret, ssh-keyscan covered every server host, and the server accepts
key-based auth for that user.
Registry authentication failed: confirm the token has the right scopes,
hasn’t expired, and the environment variable name matches what
builder.registry.password references.
Deployment timeout: raise deploy_timeout on the health check, check
jiji service logs, and confirm the container actually starts locally
first.