Continuous Delivery Using Forgejo

Posted On: 2026-08-03

By Mark

Roughly eight years ago I set up an automated deployment pipeline for this site. Aside from a handful of small improvements (ie. adding a few automated tests) and fixes (ie. migrating to a new server), I haven't put much time into updating or maintaining it. In that time, the world's changed a lot, and, as I briefly mentioned back in May, I was thinking of migrating to using Forgejo to deal with intermittent slowness/issues with my existing setup. At the time, I hadn't been in a rush to do so, but last month forced me to change plans: a protracted outage (which is still ongoing) left me with no ability to access my automated deployment system, and I was forced to upload the previous blog post manually. Since then, I've gone through the (surprisingly troublesome) process of recreating that system using Forgejo Action Runners, and I thought doing a bit of a postmortem on that process today would make for a good blog post.

Establishing A Baseline

Automated deployments for this site are made up of six steps:

  1. Creating a "package" that represents the code that will be deployed (which is organized slightly differently than what I use to write and test it locally)
  2. Automated tests to verify that package is good to deploy (including security audits, etc.)
  3. Using Git to perform the heavy lifting of uploading the package to my web host's servers (doing this manually can take hours)
  4. Using tools (provided by my web host) to take the server offline and apply the changed version of the code
  5. Verifying that everything worked as expected (automated smoke tests, etc.)
  6. Repeating steps 3-5 on different servers (the first deployment goes to a non-production server, to make sure it works right)

Fortunately, before losing access to my existing automated deployment system, I'd started the process of recreating steps 1-4 in Forgejo, so I had a (mostly accurate) representation of how the system worked before I lost access. Unfortunately, I hadn't started on the smoke tests (and I didn't have any kind of backup for them) so my only option for that was to recreate them from scratch.

Old Tech Versus New Tech

Migrating to Forgejo is supposed to be a simple, straightforward process, and one of the ways they achieve that is by following the YAML-based system used by GitHub actions as closely as possible. Unfortunately, that offered no help for my own migration, as my system was eight years old (and written using tech that was not exactly cutting-edge at the time.) Additionally, the close (but not quite) nature of Forgejo actions compared to GitHub actions means that a lot of the information on Forgejo actions defers to GitHub documentation, but there are small differences (such as how agent permissions work) that make it so that GitHub's documentation isn't quite accurate. Thus, when trying to learn to use Forgejo, one is learning two different systems, all while keeping the differences straight for systems that are intentionally designed to be confusingly similar.

The Power of Iteration

When learning a new system, it's best to start small and slowly build up knowledge. In the context of learning Forgejo actions, that meant setting up a separate project focused on learning, and incrementally adding simplified versions of the pieces that I would need in the final version. To explain via example: in order to make the deployment package, I need to have the code. Getting the code meant leveraging the GitHub Checkout action, and leveraging any third-party action meant I needed to learn how to reference and use them. Thus, I started small (just referencing an action), and slowly built towards my goal, adding in one piece at a time.

Tripping Over Everything

Part of the reason I needed to use iteration was that I kept running into strange, undocumented issues. In the interest of sparing others some of that pain, here are the issues that caused the biggest headaches:

Lastly there's an outstanding issue that I've just given up on solving, and am instead working around. As far as I can tell, it's not possible to download a release generated in an earlier step. According to the error I'm getting this is a permissions issue, but that same authentication token can upload a release without issue (which seems like it should require more permissions than a download.) If anyone knows why this is, or what I'm supposed to do to work around it, please let me know, and I'll include the info here. As it stands right now, however, this is an issue I simply don't have the time to troubleshoot.

Proof In The Pudding

As I'm writing this, I'm not 100% confident in my new deployment system. My intention is to use Forgejo actions to deploy this blog post, but I don't actually know if that will work, or how many tiny adjustments it will take to get things right. Even though I have successfully deployed (to the non-production server) using Forgejo before, that was in my separate "iteration" repository - and every step of this migration has provided surprise issues whenever I try to do something that I've already done before.

Fortunately, I can say that if you see this paragraph (instead of an account of what went wrong) then it actually did work, and I'm now using Forgejo for automated deployments.

Don't Wait and Back Up Often

As this post is (generally) serving as a retrospective for the process of migrating onto Forgejo Actions, I think this is the time to say lessons learned and what I will do differently in the future. The first lesson is one I think is best applied by others - namely, if you want to try using Forgejo Actions, don't wait until you have to; do it now. There are a lot of little things that can go wrong, and having time pressure on top of that makes the whole experience much worse. Learn from my mistake: do it ahead of time.

The second lesson is the need to back up. I use Git for a lot of things, but my old system for automated deployments was all built using editors and online tools - there were no files to store in Git. I knew that was not great, but I didn't do anything before it was too late, and I lost my smoke tests as a result (which, while not perfect, had a lot of little improvements accumulated from nearly a decade of actual use.) I've recreated what I can recall from memory, but they are less resilient compared to what I lost. To avoid having this happen again, I need to not just think about backing up, but actually do it. This means not only the automated deployment (which is now in Git and therefore easy to back up), but also the server configurations that make this possible, as well as the contents of the servers themselves. These are things I've intended to back up, but I need to go from "I should probably do this" to "it's done" before I have a repeat of what happened with my automated deployment system.

Wrapping It All Up

Thank you for reading this post about the process of migrating to using Forgejo actions for automated deployments. When I first sat down to write it, I thought I might include more of the nitty-gritty (such as example scripts or the settings on the action runner) but instead it's been mostly high level and "lessons learned". If you'd like to see me dig into those details in a future post, please let me know, but either way I hope you've enjoyed reading this and that you'll join me next month.