Deploy from Forgejo

3 min read

Teploy works with any Git remote. Forgejo is the recommended self-hosted option because its webhooks, Actions runner, container registry, and issue workflows cover the full path from source to deployment without adding a hosted dependency.

There are two ways to deploy from Forgejo:

Path Best for Trade-off
Webhook auto-deploy The fastest setup Builds on the server without a CI test gate
Forgejo Actions Teams that require tests before deploy Requires a Forgejo Actions runner and CI credentials

Fastest: webhook auto-deploy

From the project directory containing teploy.yml, run:

teploy autodeploy setup --branch main

Teploy uploads the CLI to the target server, installs a systemd-managed webhook listener, and adds a Caddy route. It then prints a webhook URL and a generated secret:

Auto-deploy configured for myapp
  Webhook URL: https://myapp.com/teploy-webhook/myapp
  Branch: main
  Secret (generated — add this to your Git provider's webhook):
    <generated-secret>

In Forgejo:

  1. Open the repository's Settings → Webhooks.
  2. Add a Forgejo webhook using the printed URL.
  3. Paste the printed secret.
  4. Enable push events.

Every push to the selected branch now runs the same deployment path as teploy deploy. The application is built on the server, health-checked, and switched over without downtime.

For a private repository, the server user must already have permission to clone and fetch it, normally through an SSH deploy key.

Check or remove the listener with:

teploy autodeploy status
teploy autodeploy remove

Test-gated: Forgejo Actions

Teploy does not install or operate a Forgejo Actions runner. If you already have a runner, add this workflow at .forgejo/workflows/deploy.yml:

name: deploy
on:
  push:
    branches: [main]

env:
  IMAGE: ${{ secrets.REGISTRY }}/myapp

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Test
        run: make test

      - name: Build and push
        run: |
          TAG="${IMAGE}:${GITHUB_SHA::12}"
          echo "${{ secrets.REGISTRY_PASSWORD }}" | docker login "${{ secrets.REGISTRY }}" -u "${{ secrets.REGISTRY_USER }}" --password-stdin
          docker build -t "$TAG" .
          docker push "$TAG"
          echo "TAG=$TAG" >> "$GITHUB_ENV"

      - name: Install Teploy
        run: |
          curl -fsSLO https://github.com/useteploy/teploy-cli/releases/latest/download/teploy_linux_amd64.tar.gz
          curl -fsSLO https://github.com/useteploy/teploy-cli/releases/latest/download/checksums.txt
          grep " teploy_linux_amd64.tar.gz$" checksums.txt > checksum.txt
          sha256sum -c checksum.txt
          tar xzf teploy_linux_amd64.tar.gz
          sudo mv teploy /usr/local/bin/

      - name: Deploy
        run: |
          install -m600 /dev/stdin deploy_key <<< "${{ secrets.TEPLOY_SSH_KEY }}"
          teploy deploy \
            --host "${{ secrets.DEPLOY_HOST }}" \
            --user "${{ secrets.DEPLOY_USER }}" \
            --key deploy_key \
            --image "$TAG"

Add these repository secrets under Settings → Actions → Secrets:

Secret Purpose
REGISTRY Registry and image namespace, such as forgejo.example.com/team
REGISTRY_USER Registry username
REGISTRY_PASSWORD Registry push credential
DEPLOY_HOST Target server IP or hostname
DEPLOY_USER SSH user on the target server
TEPLOY_SSH_KEY Dedicated private deploy key authorized on the server

If the image is private, authenticate the target server once so it can pull releases:

teploy registry login forgejo.example.com

The workflow deliberately installs Teploy from its checksummed release archive instead of piping an unverified binary into the runner.