Add SD image pipeline, documentation overhaul, and fix module issues

- Add automatic SD image builds for Raspberry Pi via Forgejo Actions
- Enable binfmt emulation on cryodev-main for aarch64 cross-builds
- Add sd-image.nix module to cryodev-pi configuration
- Create comprehensive docs/ structure with installation guides
- Split installation docs into: first-install (server), reinstall, new-client (Pi)
- Add lib/utils.nix and apps/rebuild from synix
- Fix headplane module for new upstream API (tale/headplane)
- Fix various module issues (mailserver stateVersion, option conflicts)
- Add placeholder secrets.yaml files for both hosts
- Remove old INSTRUCTIONS.md (content moved to docs/)
This commit is contained in:
steffen 2026-03-11 08:41:58 +01:00
parent a5261d8ff0
commit 5ba78886d2
44 changed files with 3570 additions and 609 deletions

View file

@ -3,6 +3,8 @@
comin = import ./comin;
forgejo = import ./forgejo;
forgejo-runner = import ./forgejo-runner;
headplane = import ./headplane;
headscale = import ./headscale;
mailserver = import ./mailserver;
nixvim = import ./nixvim;
normalUsers = import ./normalUsers;

View file

@ -18,31 +18,31 @@ in
{
config = mkIf cfg.enable {
services.forgejo = {
database.type = "postgres";
lfs.enable = true;
database.type = mkDefault "postgres";
lfs.enable = mkDefault true;
settings = {
server = {
DOMAIN = "git.${config.networking.domain}";
PROTOCOL = "http";
ROOT_URL = "https://${settings.server.DOMAIN}/";
HTTP_ADDR = "0.0.0.0";
HTTP_PORT = 3456;
SSH_PORT = head config.services.openssh.ports;
DOMAIN = mkDefault "git.${config.networking.domain}";
PROTOCOL = mkDefault "http";
ROOT_URL = mkDefault "https://${settings.server.DOMAIN}/";
HTTP_ADDR = mkDefault "0.0.0.0";
HTTP_PORT = mkDefault 3456;
SSH_PORT = mkDefault (head config.services.openssh.ports);
};
service = {
DISABLE_REGISTRATION = true;
DISABLE_REGISTRATION = mkDefault true;
};
ui = {
DEFAULT_THEME = "forgejo-dark";
DEFAULT_THEME = mkDefault "forgejo-dark";
};
actions = {
ENABLED = true;
ENABLED = mkDefault true;
};
mailer = {
ENABLED = mkDefault false;
SMTP_ADDR = "mail.${config.networking.domain}";
FROM = "git@${settings.server.DOMAIN}";
USER = "git@${settings.server.DOMAIN}";
SMTP_ADDR = mkDefault "mail.${config.networking.domain}";
FROM = mkDefault "git@${settings.server.DOMAIN}";
USER = mkDefault "git@${settings.server.DOMAIN}";
};
};
secrets = {

View file

@ -2,31 +2,30 @@
inputs,
config,
lib,
pkgs,
...
}:
let
cfg = config.services.headplane;
domain = config.networking.domain;
subdomain = cfg.reverseProxy.subdomain;
fqdn = if (cfg.reverseProxy.enable && subdomain != "") then "${subdomain}.${domain}" else domain;
headscale = config.services.headscale;
inherit (lib)
mkDefault
mkIf
;
inherit (lib.utils)
mkReverseProxyOption
mkVirtualHost
mkOption
types
;
in
{
imports = [ inputs.headplane.nixosModules.headplane ];
options.services.headplane = {
reverseProxy = mkReverseProxyOption "Headplane" "hp";
port = mkOption {
type = types.port;
default = 3000;
description = "Port for headplane to listen on";
};
};
config = mkIf cfg.enable {
@ -37,14 +36,14 @@ in
services.headplane = {
settings = {
server = {
host = mkDefault (if cfg.reverseProxy.enable then "127.0.0.1" else "0.0.0.0");
port = mkDefault 3000;
host = mkDefault "127.0.0.1";
port = mkDefault cfg.port;
cookie_secret_path = config.sops.secrets."headplane/cookie_secret".path;
};
headscale = {
url = "http://127.0.0.1:${toString headscale.port}";
public_url = headscale.settings.server_url;
config_path = "/etc/headscale/config.yaml";
url = mkDefault "http://127.0.0.1:${toString headscale.port}";
public_url = mkDefault headscale.settings.server_url;
config_path = mkDefault "/etc/headscale/config.yaml";
};
integration.agent = {
enabled = mkDefault true;
@ -53,13 +52,6 @@ in
};
};
services.nginx.virtualHosts = mkIf cfg.reverseProxy.enable {
"${fqdn}" = mkVirtualHost {
port = cfg.settings.server.port;
ssl = cfg.reverseProxy.forceSSL;
};
};
sops.secrets =
let
owner = headscale.user;

View file

@ -65,24 +65,26 @@ in
address = mkDefault (if cfg.reverseProxy.enable then "127.0.0.1" else "0.0.0.0");
port = mkDefault 8077;
settings = {
policy.path = "/etc/${acl}";
database.type = "sqlite"; # postgres is highly discouraged as it is only supported for legacy reasons
server_url = mkUrl {
policy.path = mkDefault "/etc/${acl}";
database.type = mkDefault "sqlite"; # postgres is highly discouraged as it is only supported for legacy reasons
server_url = mkDefault (mkUrl {
inherit fqdn;
ssl = with cfg.reverseProxy; enable && forceSSL;
};
derp.server.enable = cfg.reverseProxy.forceSSL;
});
derp.server.enable = mkDefault cfg.reverseProxy.forceSSL;
dns = {
magic_dns = mkDefault true;
base_domain = mkDefault "tail";
search_domains = [ cfg.settings.dns.base_domain ];
search_domains = mkDefault [ cfg.settings.dns.base_domain ];
override_local_dns = mkDefault true;
nameservers.global = optionals cfg.settings.dns.override_local_dns [
"1.1.1.1"
"1.0.0.1"
"2606:4700:4700::1111"
"2606:4700:4700::1001"
];
nameservers.global = mkDefault (
optionals cfg.settings.dns.override_local_dns [
"1.1.1.1"
"1.0.0.1"
"2606:4700:4700::1111"
"2606:4700:4700::1001"
]
);
};
};
};

View file

@ -66,8 +66,10 @@ in
fqdn = mkDefault fqdn;
domains = mkDefault [ domain ];
certificateScheme = mkDefault "acme-nginx";
stateVersion = mkDefault 1;
# stateVersion 3 requires the new mail directory structure
# For new installations, this is the correct value
# For existing installations, see: https://nixos-mailserver.readthedocs.io/en/latest/migrations.html
stateVersion = mkDefault 3;
loginAccounts = mapAttrs' (
user: accConf:
@ -79,8 +81,14 @@ in
hashedPasswordFile = config.sops.secrets."mailserver/accounts/${user}".path;
}
) cfg.accounts;
# Use ACME for certificate
x509.useACMEHost = mkDefault fqdn;
};
# ACME certificate for mail server
security.acme.certs.${fqdn} = { };
security.acme = {
acceptTerms = true;
defaults.email = mkDefault "postmaster@cryodev.xyz";

View file

@ -15,7 +15,9 @@ in
inputs.nixvim.nixosModules.nixvim
./plugins
./spellfiles.nix
# TODO: spellfiles.nix uses home-manager options (home.file, xdg.dataHome)
# which are not available in NixOS modules. Needs to be rewritten.
# ./spellfiles.nix
];
config = {

View file

@ -64,6 +64,6 @@ in
};
};
home.packages = optional (cfg.enable && plugin.servers.nixd.enable) pkgs.nixfmt;
environment.systemPackages = optional (cfg.enable && plugin.servers.nixd.enable) pkgs.nixfmt;
};
}

View file

@ -45,7 +45,7 @@ in
];
};
home.packages = optionals plugin.enable [
environment.systemPackages = optionals plugin.enable [
pkgs.ripgrep # for "live_grep"
];
};

View file

@ -30,12 +30,12 @@ in
};
# Fix for: ERROR `cc` executable not found.
home.sessionVariables = mkIf plugin.enable {
environment.sessionVariables = mkIf plugin.enable {
CC = mkDefault cc;
};
# Fix for: WARNING `tree-sitter` executable not found
home.packages = mkIf plugin.enable [
environment.systemPackages = mkIf plugin.enable [
plugin.package
];
};

View file

@ -7,7 +7,16 @@
}:
let
secrets = "${toString inputs.self}/hosts/${config.networking.hostName}/secrets/secrets.yaml";
# Check both locations for secrets.yaml
secretsInSubdir = "${toString inputs.self}/hosts/${config.networking.hostName}/secrets/secrets.yaml";
secretsInRoot = "${toString inputs.self}/hosts/${config.networking.hostName}/secrets.yaml";
secrets =
if builtins.pathExists secretsInSubdir then
secretsInSubdir
else if builtins.pathExists secretsInRoot then
secretsInRoot
else
null;
in
{
imports = [ inputs.sops-nix.nixosModules.sops ];
@ -17,5 +26,5 @@ in
sops
];
sops.defaultSopsFile = lib.mkIf (builtins.pathExists secrets) (lib.mkDefault secrets);
sops.defaultSopsFile = lib.mkIf (secrets != null) (lib.mkDefault secrets);
}