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.