Welcome to my bootstrap repository for The Jackson Family’s self-hosted infrastructure. I use this repo to bootstrap and maintain the core foundation for my home-network Infrastructure as Code (IaC)—installing Gitea on a Synology NAS, and restoring it's backup from an off-site backup on Backblaze.
It is not monolithically responsible for all (IaC) on my personal network. The Gitea recovery will restore additional IaC CI/CD repos and workflows. This is meant to ensure I always have a reliable disaster-recovery path.
The only prerequisites to performing a disaster recovery are:
- There is an available self-hosted Github runner on the home network and...
- that runner is attached to the mirror of this repo on Github.com and...
- there is a Synology NAS available on the home network...
- with the hostname defined in the Github repo's secret
SSH_NAS_HOSTand... - the "Container Manager" package (aka Docker) is compatible with this NAS...
- and there is a latest backup of the Gitea data on Backblaze in the
B2_BUCKET_NAME
.gitea/
└── workflows/
└── deploy.yml # Daily deployments via Gitea Actions
.github/
├── workflows/
│ ├── bootstrap.yml # GitHub-triggered DR bootstrap (recovery phase 1)
│ ├── common-bootstrap.yml # Core provisioning steps (reusable)
│ ├── health-check.yml # Daily dry run of DR bootstrap with Discord alert
│ ├── mirror-health.yml # Daily mirror-health check with Discord alert
│ └── restore.yml # GitHub-triggered DR restore (recovery phase 2, 🛑 DANGER, overwrites Gitea data)
playbooks/ # My Ansible playbooks & roles (NAS, Gitea, DNS, certs)
Makefile # Makefile for common tasks
- Self-hosted Deployments: Day-to-day modifications of my infrastructure are pushed to the self-hosted Gitea instance, including changes to the bootstrap infrastructure stored in this repository.
- Mirror & Monitor: Every push to the self-hosted Gitea instance of this repo are immediately mirrored to the off-site GitHub mirror (where you are probably reading this). A daily job compares the self-hosted and cloud-hosted instance and notifies my personal Discord if they have drifted.
- DR Test: Daily scheduled run on GitHub uses Ansible's check mode (with diffs) to validate playbooks. The results are posted to my personal Discord.
- On-Demand Recovery: The DR workflow is manual and two-phased — first the bare infrastructure is bootstrapped, then a gated restore of the Gitea data from its latest cloud backup.
flowchart TB
subgraph Gitea_CI["Gitea (Self-Hosted)"]
A[Push to Gitea Repo] --> B[Gitea Actions Trigger]
B --> C[Gitea Runner Executes Ansible]
end
subgraph GitHub_CI["GitHub"]
A --> E[Push Mirror to GitHub]
T["Daily Trigger ⏰"]
T --> F["Mirror Check"]
F --> G["Discord Alert"]
T --> H["Health Check (Dry Run)"]
H --> G
end
C --> D[Synology NAS Configuration]
D --> D1[Gitea Server]
D --> D2[Certbot]
D --> D3[Gitea Runner]
flowchart TB
Start([Start Recovery from GitHub])
Start -->|manual| Action_B[[Run 'Bootstrap' Action]]
Action_B -->|manual| Action_R[[Run 'Restore Gitea Data' Action]]
subgraph Restore[Restore Gitea Data]
direction LR
Approval[Wait for Approval]
Approval -->|manual| R[GitHub Runner executes playbook]
R --> M[SSH to Synology NAS]
M --> N[Fetch & Restore Backup]
N --> P[Services Back Online]
P --> P1[Gitea Server]
P --> P2[Certbot]
P --> P3[Gitea Runner]
end
Action_R -.- Restore
Details on how to contribute to this project, including how to set up a local development environment, can be found in the CONTRIBUTING.md file.
This repository uses a protected GitHub Environment to provide a manual approval gate for the disaster recovery workflow. This prevents accidental restores. If you have forked this repository, you must configure this environment in your own repository settings.
Steps:
- Navigate to your forked repository on GitHub.
- Click on the
Settingstab. - In the left sidebar, click on
Environments. - Click the
New environmentbutton. - For the name, enter
production-restore. - Click the
Configure environmentbutton. - Under Deployment protection rules, check the box for
Required reviewers. - Add your own GitHub username (or a team you belong to) as a reviewer.
- Click
Save protection rules.
In GitHub (Settings → Secrets and variables → Actions → Secrets):
| Secret | Value/Purpose |
|---|---|
SSH_KEY |
SSH private key for NAS |
NAS_SSH_PASSWORD |
NAS SSH user password |
B2_APPLICATION_KEY |
Backblaze B2 Application Key |
B2_APPLICATION_KEY_ID |
Backblaze B2 Application Key ID |
B2_BUCKET_NAME |
Backblaze B2 Bucket Name |
DISCORD_WEBHOOK |
Discord webhook for alerts |
DNSIMPLE_OAUTH_TOKEN |
DNSimple OAuth Token |
GITEA_ADMIN_PASSWORD |
Gitea Admin User Password |
GITEA_DB_PASSWORD |
Gitea Database Password |
In GitHub (Settings → Secrets and variables → Actions → Variables):
| Variable | Value/Purpose |
|---|---|
CERTBOT_EMAIL |
Certbot Email Address |
GITEA_ADMIN_USERNAME |
Gitea Admin Username |
GITEA_ADMIN_EMAIL |
Gitea Admin Email |
NAS_HOST |
FQDN/IP of NAS |
NAS_SSH_USER |
NAS SSH user |
A self-hosted GitHub runner is required to execute the disaster recovery workflows from GitHub. This runner must be on a Linux machine with Docker installed, as the bootstrap process uses a container, a feature not supported by GitHub runners on macOS or Windows. This process has been tested on Ubuntu 24.
You can set up a self-hosted runner using the provided install-runner.sh script. It's recommended to run this on a
machine separate from your NAS to ensure you can still trigger recovery even if the NAS is unavailable.
To install the runner, execute the following command in your terminal. The script will prompt you for a name for the runner and a GitHub registration token for the new runner.
bash -c "$(curl -fsSL https://raw.githubusercontent.com/jaxzin/jaxzin-infra-bootstrap/main/scripts/install-runner.sh)"After running the Bootstrap setup for the first time, Gitea will be running. To then begin self-hosting the bootstrap CI/CD, you can follow these steps:
- Go to your Gitea instance.
- Select New Migration.
- Select GitHub.
- Enter the URL of this GitHub repository.
- Do not choose "This repository will be a mirror", we will connect it as a push mirror later.
- Click Migrate Repository.
- Go to the newly created repository in Gitea.
- Click on **Settings** → **Mirroring**.
- Under **Push Mirror**, enter the URL of this GitHub repository.
- Enter your GitHub username and a personal access token with `repo` and `workflow` scope. `workflow` scope is needed to allow the push mirror to push up workflow file changes.
- Click **Add Mirror**.
- Go to Settings → Actions in Gitea.
- Add the same secrets and variables as you did for GitHub (see above).
Use this workflow to deploy changes to your Gitea instance daily.
- Runs on push.
Use this workflow to bootstrap the disaster recovery process.
- Manually triggered.
- Manually triggered.
- Gated by the
production-restoreenvironment to prevent accidental restores.
- Runs daily.
- Notifies Discord on success or failure.
- Runs daily.
- Checks freshness of mirror.
- Notifies Discord on success or failure.
This bootstrap process also installs and configures a Gitea runner on the Synology NAS. This runner is responsible for executing CI/CD workflows defined in your Gitea repositories for the majority of my home network's IaC. The GitHub runner is used for disaster recovery (DR) workflows only. The Gitea server, Certbot, and Gitea runner are all deployed as Docker containers.
- Provision replacement NAS with host name is
nas. - Ensure off-NAS GitHub runner can SSH in.
- On GitHub, run Actions → Bootstrap; then Actions → Restore Gitea Data.
- Approve if safe.
- Restore then automatically runs, unarchiving backup and restarting services.
- Monthly DR tests validate playbooks.
- Daily mirror-health alerts keep you aware.
- The most critical IaC lives on Gitea and GitHub, so hardware failures don’t lose automation.
Happy automating The Jackson Family way! 🚀
This project is licensed under the MIT License - see the LICENSE file for details.