# Deploy from Forgejo and Gitea Actions

Forgejo and Gitea Actions read GitHub-style workflow files. The differences that matter here: the workflow lives in `.forgejo/workflows/` (Gitea: `.gitea/workflows/`), the runner needs Docker, and the image goes to **your instance's package registry**, not GHCR.

Prerequisites: a runner with the label used in `runs-on` and access to a Docker daemon, a running Workload, its service instance id, the [deploy token](/guides/ci/overview#create-a-token-that-can-only-deploy), and [`zaroz-deploy.sh`](/guides/ci/shell) as `deploy/zaroz-deploy.sh`.

## Secrets and variables

In the repository (or organization) **Settings > Actions**:

| Name | Kind | Value |
|---|---|---|
| `ZAROZ_TOKEN` | secret | the `zaroz_...` deploy token |
| `ZAROZ_SERVICE` | variable | the service instance UUID |
| `REGISTRY_HOST` | variable | your instance host, for example `git.example.com` |
| `REGISTRY_USERNAME` | variable | the account that owns the packages |
| `REGISTRY_PASSWORD` | secret | an access token with `write:package` (pushes) |
| `REGISTRY_PULL_TOKEN` | secret | for a private package: an access token with `read:package` only. Zaroz uses this one to pull |

## The workflow

```yaml
# .forgejo/workflows/deploy.yml   (Gitea Actions reads .gitea/workflows/ instead; same file)
name: deploy

on:
  push:
    branches: [main]

concurrency:
  group: deploy-production
  cancel-in-progress: false

jobs:
  deploy:
    runs-on: docker
    steps:
      - uses: actions/checkout@v4
      - name: Install tools
        run: apt-get update -qq && apt-get install -y -qq curl jq
      - name: Build and push
        id: build
        env:
          REGISTRY: ${{ vars.REGISTRY_HOST }}                # git.example.com
          REGISTRY_USERNAME: ${{ vars.REGISTRY_USERNAME }}
          REGISTRY_PASSWORD: ${{ secrets.REGISTRY_PASSWORD }} # package write token
        run: |
          IMAGE="$REGISTRY/${GITHUB_REPOSITORY,,}:$(echo "$GITHUB_SHA" | cut -c1-12)"
          echo "$REGISTRY_PASSWORD" | docker login "$REGISTRY" -u "$REGISTRY_USERNAME" --password-stdin
          docker build -t "$IMAGE" .
          docker push "$IMAGE"
          echo "image=$IMAGE" >> "$GITHUB_OUTPUT"
      - name: Deploy to Zaroz
        run: sh deploy/zaroz-deploy.sh
        env:
          IMAGE: ${{ steps.build.outputs.image }}
          ZAROZ_TOKEN: ${{ secrets.ZAROZ_TOKEN }}
          ZAROZ_SERVICE: ${{ vars.ZAROZ_SERVICE }}
          # A private package needs a pull-only token, separate from the push one:
          REGISTRY_USERNAME: ${{ vars.REGISTRY_USERNAME }}
          REGISTRY_PASSWORD: ${{ secrets.REGISTRY_PULL_TOKEN }}
```


How it behaves:

- The image is `<host>/<owner>/<repo>:<12-char sha>`. A package is private by default; make it public to deploy without a pull token.
- `concurrency` queues pushes, as in the other guides.
- Zaroz pulls from inside its cluster, so the instance must be reachable from the internet over HTTPS with a valid certificate.
- If your runner has no `docker` binary, build with Kaniko or Buildah in a job container instead; only the `Build and push` step changes.

## Roll back

Re-run the workflow for an older commit from the **Actions** tab, or run the script yourself with `IMAGE=<host>/<owner>/<repo>:<old sha>`.
