- Nix 86.2%
- HCL 13.8%
| nixos | ||
| terraform | ||
| .gitignore | ||
| config.nix.example | ||
| flake.lock | ||
| flake.nix | ||
| Makefile.example | ||
| README.md | ||
forgejo-setup
A reusable Nix function that produces a complete, deployable Forgejo server configuration on DigitalOcean.
Given a host's config.nix and secrets/secrets.yaml, forgejo-setup generates everything needed to provision infrastructure and deploy NixOS:
- Terraform config — droplet, volume, firewall, DNS records
- NixOS config — Forgejo service, Caddy reverse proxy, system config
- sops-nix template files — configs with placeholders for decrypted secrets
Usage
# hosts/your-instance/flake.nix
{
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-24.11";
flake-utils.url = "github:numtide/flake-utils";
sops-nix.url = "github:Mic92/sops-nix";
};
outputs = { self, nixpkgs, flake-utils, sops-nix }:
flake-utils.lib.eachDefaultSystem (system:
let
forgejo-setup = import ../../packages/forgejo-setup {
nixpkgs = nixpkgs;
sops-nix = sops-nix;
hostConfig = ./config.nix;
secretsFile = ./secrets/secrets.yaml;
};
in {
packages = { inherit (forgejo-setup) templates; };
}
);
}
Then copy the example Makefile and customize for your host:
cp ../../packages/forgejo-setup/Makefile.example Makefile
# Edit the help text at the top to match your host name
Build and substitute:
nix build .#templates # pure build → result/ with placeholders
sops-install-secrets ./result/manifest.json # substitute secrets
cp -rL result/* ./ # copy to terraform/ and nixos/
Or using the Makefile:
make substitute-sops-secrets # build + substitute + copy
make plan # terraform plan
make deploy-nixos # substitute + rsync + nixos-rebuild
Configuration Schema
config.nix requires the following fields:
{
domain = "example.com";
subdomain = "git"; # becomes git.example.com
hostName = "forgejo"; # droplet hostname
dropletSize = "s-1vcpu-1gb";
dropletRegion = "nyc3";
dropletImage = "nixos-base";
dropletSshKeyName = "my-ssh-key";
customImageUrl = "https://...";
volumeSize = 10; # GiB
volumeName = "forgejo-data"; # DigitalOcean volume name
enableBackups = true;
s3BucketName = "my-instance-terraform-state";
s3Endpoint = "https://s3.us-west-001.backblazeb2.com";
s3BucketRegion = "us-west-001";
forgejoSiteTitle = "My Forgejo";
forgejoSiteDescription = "Self-hosted Git service";
# Optional: Forgejo data directory (default: /mnt/forgejo-data/forgejo)
# forgejoDataDir = "/mnt/forgejo-data/forgejo";
}
Secrets
Encrypted in secrets/secrets.yaml via sops:
digitalocean_token— DigitalOcean API tokencloudflare_api_token— Cloudflare API token (for DNS)cloudflare_zone_id— Cloudflare zone IDletsencrypt_email— Email for Let's Encrypt certificatesforgejo_admin_email— Admin email for Forgejos3_access_key_id— AWS-style access key for S3-compatible Terraform backend (e.g. Backblaze B2) Used by Terraform's S3 backend for remote state. Exported asAWS_ACCESS_KEY_ID.s3_secret_access_key— AWS-style secret key for S3-compatible Terraform backend Exported asAWS_SECRET_ACCESS_KEY.
What's Produced
Running nix build .#templates produces:
result/
├── manifest.json # for sops-install-secrets
├── nixos/
│ ├── flake.nix # NixOS flake for the droplet
│ ├── configuration.nix # base NixOS config (hostname, volume)
│ ├── forgejo.nix # Forgejo service module
│ ├── caddy.nix # Caddy reverse proxy module
│ ├── droplet-vars.nix # template (placeholders for secrets)
│ └── secrets.env # template (API tokens for snapshot daemon)
└── terraform/
├── main.tf # infrastructure definition
├── variables.tf # shared
├── providers.tf # shared
├── outputs.tf # shared
└── terraform.auto.tfvars.json # template (all vars with placeholders)
After sops-install-secrets, the template files have real decrypted values.
Terraform Remote State
Terraform is configured to use an S3-compatible backend for remote state
storage (via the generated backend.tf). The bucket is per-instance, configured
in config.nix as s3BucketName. Credentials (s3_access_key_id and
s3_secret_access_key) are decrypted from sops and exported as AWS_ACCESS_KEY_ID
and AWS_SECRET_ACCESS_KEY by the host's .envrc.
One-time setup
-
Create the S3-compatible bucket (e.g. Backblaze B2) with the name matching
s3BucketNamein yourconfig.nix. -
Generate an application key restricted to that bucket (read/write).
-
Add credentials to sops:
sops secrets/secrets.yamlAdd the following fields:
s3_access_key_id: "your_access_key_id" s3_secret_access_key: "your_secret_access_key" -
Migrate local state to the remote backend:
cd terraform terraform init -migrate-state
After migration, terraform plan and terraform apply use remote state
automatically. The local terraform.tfstate becomes a local cache synced
with the remote backend and can be safely deleted.
Design
forgejo-setup uses sops-nix templates:
non-secret values are inlined from config.nix at build time, while secret
values use placeholders (<SOPS:sha256:PLACEHOLDER>). A separate
sops-install-secrets step decrypts secrets.yaml and substitutes
placeholders with real values.
The derivation is fully pure — no --impure or builtins.getEnv needed.