Skip to Content
DocsGuidesCI/CD Integration

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 | sh

jiji 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 -y

Staging 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 -y

GitLab 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_PASSWORD

Required Secrets

SecretDescription
SSH_PRIVATE_KEYPrivate key for server access
SERVER_HOSTServer hostname (for ssh-keyscan)
GITHUB_TOKENRegistry authentication (if using GHCR)

SSH Key Setup

ssh-keygen -t ed25519 -C "deploy@ci" -f deploy_key

Add 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_keys

Best 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 -y

jiji 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" -y

Verify after deploying

- name: Deploy run: jiji deploy --build -y - name: Verify run: | sleep 10 jiji service logs -S api --since 1m | grep -v ERROR

Notifications

- 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.

Last updated on