Skip to main content

Forgejo Runner On TrueNAS

·1809 words·9 mins
Martin Rønn
Author
Martin Rønn
Carpenter and software developer based in Denmark doing carpentry, software development, 3D printing, CAD, self-hosting and much more.
Table of Contents

Years ago I moved most of my private code to a self-hosted Gitea instance. I liked it, but I always found it odd that the project itself does not use Gitea for its development but GitHub. Fast-forward a few years and a fork of Gitea Forgejo was born, and they do use their own product for development. Forgejo is developed by a non-profit whereas Gitea is not, and I really like their aproach to running an open source product. So a month ago I finally got around to migrate to Forgejo.

Forgejo-runner Do Not Show Up In Forgejo
#

With Gitea I used DroneCI for CICD because when I set it up that was the only viable option. Forgejo and Gitea now have its own CICD system that works much like GitHubs CICD system. So, I wanted to use that and installed the Forgejo-runner from the TrueNAS appstore. But, the runner would not show up in Forgejo. It took a while for me to figure out why. The reason it does not show up in Forgejo is because it is not on the same docker network. In order to get it on the same network a setting in the Forgejo-runner config.yml has to be changed. But, there in lies the problem. The TrueNAS app is not setup to use the config file.

Make It Use The Config
#

So, I had to install it via Docker compose. Luckly, this is very easy as TrueNAS has the option to convert an existing app to docker compose via the Edit/Convert to custom app option on the app.

Convert to custom app

Now we just have to tell Forgejo-runner to use the config.yml by changing forgejo-runner daemon to forgejo-runner -c config.yml daemon

The full Docker compose is at the end.

Make a copy of the config.yml from https://forgejo.org/docs/latest/admin/actions/runner-installation/ and put it in the data directory or run forgejo-runner generate-config > config.yml from within the dockercontainer.

Open the config.yml and find the setting

  # Specifies the network to which the container will connect.
  # Could be host, bridge or the name of a custom network.
  # If it's empty, create a network automatically.
  network: ""

Here add host to network network: "host" then save and restart the app. The Forgejo-runner should show up in Forgejo now.

Happy coding!

The Docker Compose
#

configs:
  entrypoint:
    content: |

      if [ ! -f /data/.runner ]; then
        echo "Registering the runner"
        forgejo-runner register \
          --no-interactive \
          --name runner \
          --instance <URL> \
          --token <TOKEN> || { echo "Runner failed to register"; exit 1; }
        echo "Runner registered successfully"
      else
        echo "Runner register file [/data/.runner] already exists"
        echo "Skipping registration"
        echo "If you want to re-register the runner, please delete the file /data/.runner"
      fi
      echo "\n\n"

      echo "Starting the runner"
      forgejo-runner -c config.yml daemon
      exit 0
services:
  forgejo-runner:
    image: data.forgejo.org/forgejo/runner:11
    cap_drop:
      - ALL
    configs:
      - mode: 493
        source: entrypoint
        target: /ix-entrypoint.sh
    deploy:
      resources:
        limits:
          cpus: '2'
          memory: 4096M
    entrypoint:
      - /bin/sh
      - '-c'
      - /ix-entrypoint.sh
    environment:
      GID: '568'
      GROUP_ID: '568'
      NVIDIA_VISIBLE_DEVICES: void
      PGID: '568'
      PUID: '568'
      TZ: Europe/Copenhagen
      UID: '568'
      UMASK: '002'
      UMASK_SET: '002'
      USER_ID: '568'
      dns: 192.168.2.200
    extra_hosts:
      host.docker.internal: host-gateway
    group_add:
      - 568
      - 999
    healthcheck:
      interval: 30s
      retries: 5
      start_interval: 2s
      start_period: 15s
      test: pgrep forgejo-runner
      timeout: 5s
    network_mode: host
    platform: linux/amd64
    privileged: False
    restart: unless-stopped
    security_opt:
      - no-new-privileges=true
    stdin_open: False
    tty: False
    user: '568:568'
    volumes:
      - bind:
          create_host_path: False
          propagation: rprivate
        read_only: False
        source: /mnt/dockerdata/forgejorunner/data
        target: /data
        type: bind
      - bind:
          create_host_path: False
          propagation: rprivate
        read_only: False
        source: /var/run/docker.sock
        target: /var/run/docker.sock
        type: bind
volumes: {}
x-notes: >+
  # Forgejo Runner


  ## Bug Reports and Feature Requests


  If you find a bug in this app or have an idea for a new feature, please file
  an issue at

  https://github.com/truenas/apps

x-portals: []

The Config
#


# Example configuration file, it's safe to copy this as the default config file without any modification.

# You don't have to copy this file to your instance,
# just run `forgejo-runner generate-config > config.yaml` to generate a config file.

#
# The value of level or job_level can be trace, debug, info, warn, error or fatal
#
log:
  #
  # What is displayed in the output of the runner process but not sent
  # to the Forgejo instance.
  #
  level: info
  #
  # What is sent to the Forgejo instance and therefore
  # visible in the web UI for a given job.
  #
  job_level: info

runner:
  # Where to store the registration result.
  file: .runner
  # Execute how many tasks concurrently at the same time.
  capacity: 1
  # Extra environment variables to run jobs.
  envs:
    A_TEST_ENV_NAME_1: a_test_env_value_1
    A_TEST_ENV_NAME_2: a_test_env_value_2
  # Extra environment variables to run jobs from a file.
  # It will be ignored if it's empty or the file doesn't exist.
  env_file: .env
  # The timeout for a job to be finished.
  # Please note that the Forgejo instance also has a timeout (3h by default) for the job.
  # So the job could be stopped by the Forgejo instance if it's timeout is shorter than this.
  timeout: 3h
  # The timeout for the runner to wait for running jobs to finish when
  # shutting down because a TERM or INT signal has been received.  Any
  # running jobs that haven't finished after this timeout will be
  # cancelled.
  # If unset or zero the jobs will be cancelled immediately.
  shutdown_timeout: 3h
  # Whether skip verifying the TLS certificate of the instance.
  insecure: false
  # The timeout for fetching the job from the Forgejo instance.
  fetch_timeout: 5s
  # The interval for fetching the job from the Forgejo instance.
  fetch_interval: 2s
  # The interval for reporting the job status and logs to the Forgejo instance.
  report_interval: 1s
  # The labels of a runner are used to determine which jobs the runner can run, and how to run them.
  # Like: ["macos-arm64:host", "ubuntu-latest:docker://node:20-bookworm", "ubuntu-22.04:docker://node:20-bookworm"]
  # If it's empty when registering, it will ask for inputting labels.
  # If it's empty when executing the `daemon`, it will use labels in the `.runner` file.
  labels: []

cache:
  #
  # When enabled, workflows will be given the ACTIONS_CACHE_URL environment variable
  # used by the https://code.forgejo.org/actions/cache action. The server at this
  # URL must implement a compliant REST API and it must also be reachable from
  # the container or host running the workflows.
  #
  # See also https://forgejo.org/docs/next/user/actions/advanced-features/#cache
  #
  # When it is not enabled, none of the following options apply.
  #
  # It works as follows:
  #
  # - the workflow is given a one time use ACTIONS_CACHE_URL
  # - a cache proxy listens to ACTIONS_CACHE_URL
  # - the cache proxy securely communicates with the cache server using
  #   a shared secret
  #
  enabled: true
  #
  #######################################################################
  #
  # Only used for the internal cache server.
  #
  # If external_server is not set, the Forgejo runner will spawn a
  # cache server that will be used by the cache proxy.
  #
  #######################################################################
  #
  # The port bound by the internal cache server.
  # 0 means to use a random available port.
  #
  port: 0
  #
  # The directory to store the cache data.
  #
  # If empty, the cache data will be stored in $HOME/.cache/actcache.
  #
  dir: ""
  #
  #######################################################################
  #
  # Only used for the external cache server.
  #
  # If external_server is set, the internal cache server is not
  # spawned.
  #
  #######################################################################
  #
  # The URL of the cache server. The URL should generally end with
  # "/". The cache proxy will forward requests to the external
  # server. The requests are authenticated with the "secret" that is
  # shared with the external server.
  #
  external_server: ""
  #
  # The shared cache secret used to secure the communications between
  # the cache proxy and the cache server.
  #
  # If empty, it will be generated to a new secret automatically when
  # the server starts and it will stay the same until it restarts.
  #
  secret: ""
  #
  #######################################################################
  #
  # Common to the internal and external cache server
  #
  #######################################################################
  #
  # The IP or hostname (195.84.20.30 or example.com) to use when constructing
  # ACTIONS_CACHE_URL which is the URL of the cache proxy.
  #
  # If empty it will be detected automatically.
  #
  # If the containers or host running the workflows reside on a
  # different network than the Forgejo runner (for instance when the
  # docker server used to create containers is not running on the same
  # host as the Forgejo runner), it may be impossible to figure that
  # out automatically. In that case you can specifify which IP or
  # hostname to use to reach the internal cache server created by the
  # Forgejo runner.
  #
  host: ""
  #
  # The port bound by the internal cache proxy.
  # 0 means to use a random available port.
  #
  proxy_port: 0
  #
  # Overrides the ACTIONS_CACHE_URL variable passed to workflow
  # containers. The URL should generally not end with "/". This should only
  # be used if the runner host is not reachable from the workflow containers,
  # and requires further setup.
  #
  actions_cache_url_override: ""

container:
  # Specifies the network to which the container will connect.
  # Could be host, bridge or the name of a custom network.
  # If it's empty, create a network automatically.
  network: "host"
  # Whether to create networks with IPv6 enabled. Requires the Docker daemon to be set up accordingly.
  # Only takes effect if "network" is set to "".
  enable_ipv6: false
  # Whether to use privileged mode or not when launching task containers (privileged mode is required for Docker-in-Docker).
  privileged: false
  # And other options to be used when the container is started (eg, --volume /etc/ssl/certs:/etc/ssl/certs:ro).
  options:
  # The parent directory of a job's working directory.
  # If it's empty, /workspace will be used.
  workdir_parent:
  # Volumes (including bind mounts) can be mounted to containers. Glob syntax is supported, see https://github.com/gobwas/glob
  # You can specify multiple volumes. If the sequence is empty, no volumes can be mounted.
  # For example, if you only allow containers to mount the `data` volume and all the json files in `/src`, you should change the config to:
  # valid_volumes:
  #   - data
  #   - /etc/ssl/certs
  # If you want to allow any volume, please use the following configuration:
  # valid_volumes:
  #   - '**'
  valid_volumes: []
  # overrides the docker client host with the specified one.
  # If "-" or "", an available docker host will automatically be found.
  # If "automount", an available docker host will automatically be found and mounted in the job container (e.g. /var/run/docker.sock).
  # Otherwise the specified docker host will be used and an error will be returned if it doesn't work.
  docker_host: "-"
  # Pull docker image(s) even if already present
  force_pull: false
  # Rebuild local docker image(s) even if already present
  force_rebuild: false

host:
  # The parent directory of a job's working directory.
  # If it's empty, $HOME/.cache/act/ will be used.
  workdir_parent:

Related

Obsidian + Syncthing = ❤️
The Cost of Self Hosting
·1064 words·5 mins
I Moved From Plex to Jellyfin
·759 words·4 mins