๐Ÿ“— Forgejo#

services:

  postgres:
    image: postgres:16
    container_name: forgejo-postgres
    pull_policy: missing
    restart: unless-stopped
    environment:
      POSTGRES_USER: forgejo
      POSTGRES_PASSWORD: sdfghjkl
      POSTGRES_DB: forgejo
    volumes:
      - ./data/postgres:/var/lib/postgresql/data
    networks:
      - forgejo

  forgejo:
    image: codeberg.org/forgejo/forgejo:16
    pull_policy: missing
    container_name: forgejo
    restart: unless-stopped
    environment:
      USER_UID: "1000"
      USER_GID: "1000"
      # Forgejo รฉcoute en HTTP dans le rรฉseau Docker.
      FORGEJO__server__PROTOCOL: http
      FORGEJO__server__HTTP_ADDR: 0.0.0.0
      FORGEJO__server__HTTP_PORT: "3000"
      # URL publique, avec le TLS portรฉ par HAProxy.
      FORGEJO__server__DOMAIN: forge.guisam.fr
      FORGEJO__ssh__DOMAIN: forge
      FORGEJO__server__ROOT_URL: https://forge.guisam.fr/
      # La base PostgreSQL
      FORGEJO__database__DB_TYPE: postgres
      FORGEJO__database__HOST: postgres:5432
      FORGEJO__database__NAME: forgejo
      FORGEJO__database__USER: forgejo
      FORGEJO__database__PASSWD: sdfghjkl
      # Active Forgejo Actions
      FORGEJO__actions__ENABLED: "true"
    volumes:
      - ./data/forgejo:/data
      - /etc/localtime:/etc/localtime:ro
    # Pas de publication du port 3000 :
    # HAProxy doit uniquement atteindre Anubis.
    expose:
      - "3000"
    ports:
      - "222:22"
    networks:
      - forgejo
    depends_on:
      - postgres


  anubis:
    image: ghcr.io/techarohq/anubis:latest
    pull_policy: missing
    container_name: forgejo-anubis
    restart: unless-stopped
    environment:
      # Anubis รฉcoute en HTTP.
      BIND: ":8080"
      # Forgejo derriรจre Anubis.
      TARGET: "http://forgejo:3000"
      # Difficultรฉ PoW.
      DIFFICULTY: "4"
      # Permet notamment ร  Anubis de servir robots.txt.
      SERVE_ROBOTS_TXT: "true"
      # Important derriรจre HAProxy :
      # Anubis doit considรฉrer HTTPS comme le protocole public.
      COOKIE_SECURE: "true"
    expose:
      - "8080"
    ports:
      - "8080:8080"
    networks:
      - forgejo
    depends_on:
      - forgejo


  # Docker-in-Docker utilisรฉ par le runner.
  #
  # Cela รฉvite de donner directement /var/run/docker.sock
  # de l'hรดte au runner.
  docker:
    image: docker:27-dind
    pull_policy: missing
    container_name: forgejo-runner-dind
    restart: unless-stopped

    privileged: true

    command:
      - --tls=false
      - --host=tcp://0.0.0.0:2375

    environment:
      DOCKER_TLS_CERTDIR: ""

    volumes:
      - ./data/runner-docker:/var/lib/docker

    networks:
      - runner


  # runner:
  #   image: code.forgejo.org/forgejo/runner:6
  #   container_name: forgejo-runner
  #   restart: unless-stopped

  #   depends_on:
  #     - forgejo
  #     - docker

  #   environment:
  #     # URL interne : le runner n'a pas besoin de passer
  #     # par HAProxy ni par Anubis.
  #     FORGEJO_INSTANCE: "http://forgejo:3000"

  #     # ร€ remplacer par le token gรฉnรฉrรฉ dans Forgejo.
  #     RUNNER_TOKEN: "CHANGE_ME"

  #     RUNNER_NAME: "docker-runner-01"

  #   volumes:
  #     - ./data/runner:/data

  #   networks:
  #     - forgejo
  #     - runner

  #  # Le runner doit utiliser le Docker-in-Docker.
  #  extra_hosts:
  #    - "dind:host-gateway"


networks:
  forgejo:
    driver: bridge

  runner:
    driver: bridge

Tip

Generate self-signed certificate For testing purposesโ€ฆ

แ… openssl req -x509 -nodes -newkey rsa:4096 -keyout forgejo.key \
-out forgejo.pem -subj="/CN=gforge" -addext "subjectAltName = DNS:forgejo, IP:82.64.232.18"
# copy self-signed cert
แ… cp forgejo.pem forgejo.key forgejo/gitea/conf
# or le cert
แ… sudo cp /etc/letsencrypt/live/forge.guisam.fr/{fullchain,privkey}.pem forgejo/gitea/conf

Tip

See #cerbot-dns-ovh to generate certificate.

Update configuration variables with certificate/key and domain

แ… grep -ni "domain\|protocol\|root_url\|_file" forgejo/gitea/conf/app.ini
18:DOMAIN = forge.guisam.fr
19:SSH_DOMAIN = forge
21:ROOT_URL = https://forge.guisam.fr:3000/
28:PROTOCOL = https
29:CERT_FILE = /data/gitea/conf/fullchain.pem
30:KEY_FILE =  /data/gitea/conf/privkey.pem
แ… dig forge +noall +answer
forge.                  0       IN      A       192.168.94.62
แ… dig forge.guisam.fr +noall +answer
forge.guisam.fr.        982     IN      A       82.64.232.18

Customize the homepage:

แ… mkdir forgejo/gitea/templates
แ… cat <<EOF > forgejo/gitea/templates/home.tmpl
{{template "base/head" .}}
<div role="main" aria-label="{{if .IsSigned}}{{ctx.Locale.Tr "dashboard"}}{{else}}{{ctx.Locale.Tr "home"}}{{end}}" class="page-content home">
        <div class="tw-mb-8 tw-px-8">
                <div class="center">
                        <img class="logo" width="220" height="220" src="{{AssetUrlPrefix}}/img/logo.svg" alt="{{ctx.Locale.Tr "logo"}}">
                        <div class="hero">
                                <h1 class="ui icon header title">
                                        {{AppDisplayName}}
                                </h1>
                                <h2> ๐ŸŽ ๐ŸคŸ ๐Ÿค“ </h2>
                        </div>
                </div>
        </div>
</div>
EOF
แ… dck restart server

Forgejo Actions Workflows โ€“ Detailed Overview#

Forgejo Actions enable automated CI/CD pipelines using YAML-defined workflows in .forgejo/workflows/. These workflows respond to repository events and execute jobs on available runners.

Workflow Triggers and Structure#

Workflows are defined in .yaml files and activated by events like:

  • push: On code pushes to specific branches or tags (e.g., tags: - 'v*.*.*').

  • pull_request: On pull request creation or updates.

  • workflow_dispatch: Manual trigger via UI or API.

Example:

on:
  push:
    tags:
      - 'v*.*.*'
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: echo "Building version ${{ github.ref_name }}"

Job Execution and Environment#

Jobs run on runners labeled via runs-on (e.g., ubuntu-latest, docker). You can:

  • Use containers: Define a container.image (e.g., node:24.4) for isolated execution.

  • Access context variables:

    • ${{ forgejo.ref_name }}: Branch or tag name.

    • ${{ forgejo.sha }}: Commit hash.

    • ${{ secrets.API_KEY }}: Encrypted secrets stored in repository settings.

Real-World Use Cases#

  1. Build and Push Docker Images Triggered on tag creation, builds an image and pushes to a private registry using docker/build-push-action.

  2. Test and Deploy to Staging On push to development, runs tests and deploys to a test server via SSH.

  3. Artifact Upload Save build outputs using actions/upload-artifact@v3 for later use.

Secrets and Security#

  • Store sensitive data (passwords, tokens) as secrets in repository or instance settings.

  • Use ${{ secrets.VAR_NAME }} in workflows.

  • Avoid exposing secrets in logs; use non-root users inside containers when possible.

Runner Setup#

  • Runners can be self-hosted or managed at the instance level.

  • Register via Settings > Actions > Runners using a token.

  • Ensure runner labels match runs-on in workflows.