While this setup gives me full control and reproducibility, it’s not without its quirks. Below are some of the key issues I’ve faced — and how I’ve worked around them.

Chicken <-> egg problem

Issue:

Everything in the homelab is automated through GitOps and Gitea Actions — but Gitea itself is part of the setup. So what happens if Gitea is down or wiped? I’d lose the ability to run any automation at all.

Workaround:

To solve this, I treat Gitea as a bootstrap component. Its recovery is handled with a dedicated recovery playbook that can be run manually via Ansible, outside of the GitOps way of working.

Once Gitea is back online and runners are up, the rest of the setup can “snowball” back into place.

Host specific changes

Issue:

My Gitea Actions are smart enough to trigger based on role changes or group variable updates. But they don’t detect changes to individual host_vars, meaning I have to manually redeploy in those cases.

Workaround:

For now, I trigger those roles manually when needed. This is rare — most configuration is defined at the group level — but I’m considering a more dynamic Gitea Actions matrix or tagging-based approach in the future.

I already tried out some potential solutions but got lost in a rabbithole with weird errors and was losing too much time.

Secret management

Issue:

Storing sensitive variables like SSH keys, API tokens, and Vault credentials can be tricky.

Workaround:

Secrets are currently managed via Gitea’s CI secrets, injected at runtime. On top of that, Vault is used for storing sensitive runtime values — though its recovery depends on a separate flow (also covered in recovery logic).

I also cheat sometimes by setting a secret directly in the variables… Woops… But as long as my repo is private I don’t really care. The next step is to see how I can utilize my Vault instance to store the secrets and call them in the playbooks.

CI Runner Bottleneck

Issue:

Your automation depends heavily on Gitea runners — but what happens if they go down or are misconfigured? You could end up in a situation where changes push to Gitea but never deploy.

Workaround:

Initially there will only be 1 runner that gets installed using the gitea role. After the provisioning + configuration of the k3s cluster, there will be more runners available to use. Having the runners on the k3s cluster more scalable and easier to maintain.