02-262000
93
1853
24109
7
7024
322
4149
86
05
21509
68417
80
2048
319825
46233
05
2014
30986
585101
25403
31219
752
0604
21048
293612
534082
206
2107853
12201972
24487255
30412
98
4024161
888
35045462
41520257
33
56
04
69
41
15
25
65
21
0223
688
28471
21366
8654
31
1984
272
21854
633
51166
41699
6188
15033
21094
32881
26083
2143
406822
81205
91007
38357
110
2041
312
57104
00708
12073
688
21982
20254
55
38447
26921
285
30102
21604
15421
25
3808
582031
62311
85799
87
6895
72112
101088
604122
126523
86801
8447
210486
LV426
220655
272448
29620
339048
31802
9859
672304
581131
338
70104
16182
711632
102955
2061
5804
850233
833441
465
210047
75222
98824
63
858552
696730
307124
58414
209
808044
331025
62118
2700
395852
604206
26
309150
885
210411
817660
121979
20019
462869
25002
308
52074
33
80544
1070
020478
26419
372122
2623
79
90008
8049
251664
900007
704044
982365
25819
385
656214
409
218563
527222
80106
1314577
39001
7162893
12855
57
23966
4
6244009
2352
308
928
2721
8890
402
540
795
23
66880
8675309
821533
249009
51922
600454
9035768
453571
825064
131488
641212
218035
37
6022
82
572104
799324
4404
8807
4481
8915
2104
1681
326
446
8337
526
593
8057
22
23
6722
890
2608
7274
2103
03-111968
04-041969
05-1701D
06-071984
07-081940
08-47148
09-081966
10-31

Setting Up Forgejo Actions

Forgejo

First off, if you are not familiar with it, Forgejo is an opensource fork of Gitea, which is meant as a sort of Github replacement that you can host yourself. Gitea itself is nice, and I was running that myself when I learned about some drama within the community. I forget the details but essentially Gitea was moving towards a more corporate structure and not supporting the wishes and needs of the larger community. Forgejo was forked from the Gitea source and remains a community directed project, so I decided to pull the plug and move to Forgejo. I would do a write-up about that migration, but it was honestly so easy I 'm not sure there's much to say. The Forgejo documentation is pretty high quality so my switchover was totally painless.

Actions

So you want to run CI/CD on your self-hosted Forgejo instance. The feature exists, it's called Forgejo Actions, and it's supposed to be GitHub Actions-compatible. Here was my experience setting up Forgejo Actions for use within a complete CI/CD pipeline using Forgejo, ArgoCD and Kubernetes.

What Is Going On

Forgejo Actions uses act_runner, which is basically the open source equivalent of GitHub's runner. It executes workflows defined in .forgejo/workflows/ (or .gitea/workflows/ - they're the same thing). The syntax is GitHub Actions YAML, which is nice if you already know that ecosystem. The catch: it's not as polished as GitHub Actions. Expect to read error messages carefully.

Installing the Runner

First, enable Actions in your Forgejo config (app.ini):

[actions]
ENABLED = true

Restart Forgejo. You should now see an Actions section in your site admin panel. Download the runner binary from the Gitea releases (yes, Gitea, not Forgejo - they share the runner):

wget https://dl.gitea.com/act_runner/0.3.0/act_runner-0.3.0-linux-amd64
chmod +x act_runner-0.3.0-linux-amd64
sudo mv act_runner-0.3.0-linux-amd64 /usr/local/bin/act_runner

Registering the Runner

Go to your Forgejo admin panel → Actions → Runners → Create new Runner. Copy the registration token.

Register the runner:

act_runner register --instance https://your-forgejo.com --token YOUR_TOKEN

This creates a .runner file. Don't edit it directly - it's JSON and gets regenerated when the runner restarts.

Start the runner:

act_runner daemon

It'll just work. Probably. Check your Forgejo admin panel to see if it shows up as active.

Making It Persistent

Systemd service:

[Unit]
Description=Forgejo Actions Runner
After=network.target

[Service]
Type=simple
User=your-user
WorkingDirectory=/home/your-user
ExecStart=/usr/local/bin/act_runner daemon
Restart=always

[Install]
WantedBy=multi-user.target

Standard systemd stuff, if you're into that sort of thing. Enable it, start it, forget about it.

Docker

I was trying to go fast, and here's where I ran into a couple of issues. Your workflows run inside Docker containers (by default). If your workflow needs to build Docker images, you need Docker-in-Docker, which means mounting the Docker socket. Create a config file ~/.act_runner_config.yaml:

container:
  network: bridge
  privileged: false
  options: "-v /var/run/docker.sock:/var/run/docker.sock"
  valid_volumes:
    - "/var/run/docker.sock"

Update your systemd service to use it:

ExecStart=/usr/local/bin/act_runner daemon --config /home/your-user/.act_runner_config.yaml

The runner will now mount the Docker socket into workflow containers. Whether this works depends on your specific setup. Test it.

A Basic Workflow

.forgejo/workflows/build.yml:

name: Build

on:
  push:
    branches: [main]

jobs:
  build:
    runs-on: runner-name
    container:
      image: docker:latest
    steps:
      - name: Install git
        run: apk add --no-cache git
      
      - name: Checkout
        run: |
          git clone https://your-forgejo.com/user/repo.git .
          git checkout main
      
      - name: Build something
        run: docker build -t myimage:latest .

Note: actions/checkout@v3 (and other github actions) needs Node.js, which many lightweight containers don't have. Cloning manually with git works fine, which is what I ended up doing to minimize the number of containers I was spawning here.

Architecture Gotchas

If your runner is ARM (Raspberry Pi, M1 Mac, etc.) but your deployment target is AMD64, you'll get an exec format error when the image tries to run. Ask me how I know, lol. Build for the right architecture:

- name: Build
  run: docker buildx build --platform linux/amd64 -t myimage:latest --push .

The Infinite Loop

If your workflow updates files and pushes them back to the repo, it'll trigger itself again. Forever. I was pretty sure I was going to destroy my VM when I triggered this one. Haha. Fix:

on:
  push:
    branches: [main]
    paths-ignore:
      - 'path/to/auto-updated/files/**'

Secrets

Repo Settings → Secrets → Actions. Add your tokens there. Reference them as $ in workflows. Standard stuff. Make sure tokens have the right permissions. A token with only read access won't be able to push commits or images back.

Is It Worth It?

That depends. If you're already running Forgejo and want CI/CD without depending on external services, yes. It works. The debugging experience is rougher than GitHub Actions because there's less documentation and fewer examples floating around, but the core functionality is solid. The GitHub Actions compatibility is real - most actions that don't require GitHub-specific APIs will work. The ecosystem is there. For me the main drive to get this working was to get independance from the whole GitHub/Microsoft environment, and still have some of the niceties from GH. This sovereignty feels like a big benefit, but it's mostly just in peace of mind.