Vetrix Docs

Repository Mirroring

Vetrix supports two mirroring modes:

  • Pull mirror — Vetrix periodically fetches from a remote source. The local repo stays in sync with an upstream (e.g., a GitHub repo).
  • Push mirror — After every push to Vetrix, changes are forwarded to a remote destination.

Pull mirrors

Creating a pull mirror

curl -X POST https://<vetrix-host>/api/v1/repos/<owner>/<repo>/mirrors \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{
    "direction": "pull",
    "remote_url": "https://github.com/upstream/repo.git",
    "interval_minutes": 60,
    "credentials": {
      "username": "git",
      "password": "<github-pat>"
    }
  }'

Vetrix will fetch all branches and tags from the remote on the configured interval.

Force-syncing now

curl -X POST https://<vetrix-host>/api/v1/repos/<owner>/<repo>/mirrors/<mirror-id>/sync \
  -H "Authorization: Bearer <token>"

Conflict behaviour

Pull mirrors use --force when updating refs. Local changes to mirrored branches will be overwritten on the next sync. If you need to modify mirrored content, disable the mirror first.

Push mirrors

Creating a push mirror

curl -X POST https://<vetrix-host>/api/v1/repos/<owner>/<repo>/mirrors \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{
    "direction": "push",
    "remote_url": "https://gitlab.com/org/repo.git",
    "credentials": {
      "username": "oauth2",
      "password": "<gitlab-pat>"
    }
  }'

Push mirrors trigger immediately after each push event. They are best-effort: if the remote is unreachable, Vetrix retries up to 3 times with exponential back-off, then marks the mirror as error.

SSH credentials

For SSH remotes, use ssh:// URLs and provide an SSH private key instead of a username/password:

curl -X POST https://<vetrix-host>/api/v1/repos/<owner>/<repo>/mirrors \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{
    "direction": "pull",
    "remote_url": "ssh://git@github.com/upstream/repo.git",
    "interval_minutes": 30,
    "credentials": {
      "ssh_private_key": "-----BEGIN OPENSSH PRIVATE KEY-----\n..."
    }
  }'

Private keys are encrypted at rest using the instance encryption key (MIRROR_ENC_KEY environment variable). The raw key is never stored.

Listing mirrors

curl https://<vetrix-host>/api/v1/repos/<owner>/<repo>/mirrors \
  -H "Authorization: Bearer <token>"

Response:

[
  {
    "id": "01HZ...",
    "direction": "pull",
    "remote_url": "https://github.com/upstream/repo.git",
    "interval_minutes": 60,
    "status": "ok",
    "last_sync_at": "2026-04-11T12:00:00Z",
    "last_error": null
  }
]

Mirror status values

Status Meaning
pending Waiting for first sync
syncing Sync in progress
ok Last sync completed successfully
error Last sync failed; last_error contains the message

Deleting a mirror

curl -X DELETE https://<vetrix-host>/api/v1/repos/<owner>/<repo>/mirrors/<mirror-id> \
  -H "Authorization: Bearer <token>"

Deleting a mirror stops future syncs but does not alter the repository content.

API reference

Method Path Description
GET /api/v1/repos/:owner/:repo/mirrors List mirrors
POST /api/v1/repos/:owner/:repo/mirrors Create mirror
GET /api/v1/repos/:owner/:repo/mirrors/:id Get mirror
PATCH /api/v1/repos/:owner/:repo/mirrors/:id Update mirror
DELETE /api/v1/repos/:owner/:repo/mirrors/:id Delete mirror
POST /api/v1/repos/:owner/:repo/mirrors/:id/sync Force sync now

Permissions

Creating and managing mirrors requires maintain role or higher on the repository, or admin privileges.

Admin configuration

Admins can set the global mirror sync scheduler interval and disable mirroring entirely. See ../admin-docs/admin/mirrors.md for instance-level mirror settings.