https://github.com/ryanmalonzo/homelab
Babylon, my personal homelab orchestrated using Docker Compose 🐳 and GitOps 🚀
https://github.com/ryanmalonzo/homelab
devops docker docker-compose gitops homelab komodo lxc proxmox proxmox-ve selfhosted sysadmin tailscale
Last synced: about 2 months ago
JSON representation
Babylon, my personal homelab orchestrated using Docker Compose 🐳 and GitOps 🚀
- Host: GitHub
- URL: https://github.com/ryanmalonzo/homelab
- Owner: ryanmalonzo
- Created: 2025-02-20T21:23:12.000Z (over 1 year ago)
- Default Branch: main
- Last Pushed: 2025-03-03T18:55:54.000Z (over 1 year ago)
- Last Synced: 2025-03-03T19:39:21.032Z (over 1 year ago)
- Topics: devops, docker, docker-compose, gitops, homelab, komodo, lxc, proxmox, proxmox-ve, selfhosted, sysadmin, tailscale
- Homepage: https://ryanmalonzo.com
- Size: 71.3 KB
- Stars: 0
- Watchers: 1
- Forks: 0
- Open Issues: 1
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
README
# chaldea

My self-deploying homelab infrastructure using NixOS and Terraform. Automated backups, continuous deployment, monitoring, version control.
## Structure
```
.
├── .github/workflows/
│ ├── ci.yaml
│ ├── cd.yaml
│ └── ...
├── modules/
│ ├── backups.nix # restic to backblaze b2
│ ├── caddy.nix
│ ├── dns.nix
│ ├── networking.nix
│ ├── virtualisation.nix
│ ├── zfs.nix
│ └── ...
├── services/ # individual service definitions
├── terraform/ # cloudflare dns records
├── secrets/ # sops-encrypted secrets
├── configuration.nix
├── flake.nix
└── hardware-configuration.nix
```
## Tech stack
- **NixOS** with flakes — declarative system configuration
- **Podman** — container runtime
- **ZFS** — storage pool
- **Terraform** — infrastructure as code for DNS records
- **sops-nix** — encrypted secrets in version control
- **GitHub Actions** — CI/CD using self-hosted runner
## Deployment workflow
> The GitHub Actions runner runs on chaldea and deploys to itself. The host rebuilds its own configuration on every merge. Zero manual deployments.
### Cloud providers
- **Backblaze B2** — backup storage for restic
- **Tailscale** — VPN mesh network
- **Pangolin** — self-hosted VPS (unmanaged)
- **Cloudflare** — DNS management via Terraform
- **Healthchecks.io** — heartbeat monitor for Gatus
### CI/CD pipeline
The GitHub Actions runner executes **on chaldea itself**.
1. Pull requests trigger validation: formatting checks, flake correctness, and terraform plan
2. Terraform plan output is posted as a PR comment when DNS changes are detected
3. Merges to `main` trigger deployment: DNS updates followed by system configuration
4. PRs labeled with `skip-deploy` bypass the deployment step
**The entire system state is version controlled.**
## Adding a new service
### Internal service (behind Tailscale)
**1. Create `services/.nix`**
```nix
{ ... }:
{
systemd.tmpfiles.rules = [
"d /srv//data 0755 1000 100 -"
];
virtualisation.oci-containers.containers. = {
image = "image/name:tag";
networks = [ "" ];
ports = [ ":" ];
volumes = [
"/srv//data:/data"
];
environment = {
TZ = "Europe/Paris";
};
};
systemd.services."podman-".after = [ "podman-network-.service" ];
systemd.services."podman-".requires = [ "podman-network-.service" ];
}
```
**2. Add the network — `modules/container-networks.nix`**
```nix
networks = [
...
""
];
```
**3. Add the Caddy virtual host — `modules/caddy.nix`**
```nix
virtualHosts = {
...
".internal.chaldea.dev" = {
extraConfig = ''
${tlsConfig}
reverse_proxy localhost:
'';
};
};
```
**4. Import the module — `flake.nix`**
```nix
modules = [
...
./services/.nix
];
```
### Public service
**1. Create `services/.nix`**
```nix
{ ... }:
{
virtualisation.oci-containers.containers. = {
image = "image/name:tag";
networks = [ "proxy" ];
};
systemd.services."podman-".after = [ "podman-network-proxy.service" ];
systemd.services."podman-".requires = [ "podman-network-proxy.service" ];
}
```
**2. Add a CNAME record — `terraform/main.tf`**
```hcl
cname_subdomains = [
...
""
]
```
**3. Import the module — `flake.nix`**
```nix
modules = [
...
./services/.nix
];
```
### Adding a sops secret
**1. Add the plaintext value to `secrets/secrets.yaml`**
```sh
sops secrets/secrets.yaml
```
**2. Reference the secret in the service**
```nix
{ config, ... }:
{
sops.secrets. = { };
sops.templates."-env" = {
content = ''
ENV_VAR=${config.sops.placeholder.}
'';
};
virtualisation.oci-containers.containers. = {
...
environmentFiles = [ config.sops.templates."-env".path ];
};
# Restart the container when the secret changes
systemd.services."podman-".restartTriggers = [
config.sops.templates."-env".file
];
}
```
### Adding a Gatus monitor
Add an endpoint to the `endpoints` list in `services/gatus.nix`:
```nix
endpoints = [
...
{
name = "";
group = "";
url = "https://.internal.chaldea.dev";
interval = "30s";
conditions = [ "[STATUS] == 200" ];
alerts = [ { type = "ntfy"; } ];
}
];
```
### GitHub configuration
Secrets and variables configured at the repository level:
| Name | Type | Description | Example |
| --------------------------- | -------- | ------------------------------------------- | ------------------------------------- |
| `AWS_ACCESS_KEY_ID` | Secret | Backblaze B2 access key for terraform state | `0001a2b3c4d5e6f7g8h9` |
| `AWS_SECRET_ACCESS_KEY` | Secret | Backblaze B2 secret key for terraform state | `K001abcdefghijklmnopqrstuvwxyz` |
| `CLOUDFLARE_API_TOKEN` | Secret | Cloudflare API token for DNS management | `abc123...` |
| `SSH_PRIVATE_KEY` | Secret | Ed25519 private key for deployment | `-----BEGIN OPENSSH PRIVATE KEY-----` |
| `TF_VAR_cloudflare_zone_id` | Variable | Cloudflare zone ID for DNS records | `abc123def456ghi789jkl012mno345` |
| `TF_VAR_tailscale_ip` | Variable | Tailscale IP address of chaldea | `100.x.y.z` |
| `TF_VAR_pangolin_ip` | Variable | Public IP of pangolin VPS | `1.2.3.4` |