Add nginx,traefik,caddy,docker,podman

This commit is contained in:
Alexander Derevianko
2025-07-26 22:53:41 +02:00
parent 945b8ade7a
commit 5a3ef4684b
14 changed files with 494 additions and 23 deletions
+47 -3
View File
@@ -4,7 +4,7 @@
#+OPTIONS: toc:t num:nil #+OPTIONS: toc:t num:nil
*Abstract* *Abstract*
This guide documents the process for a minimal installation of NixOS on a Proxmox virtual machine. It leverages the =nixos-anywhere= tool for remote deployment and =disko= for declarative disk partitioning. It also covers the essential post-installation steps for integrating the new host with =sops-nix= for secrets management. This guide documents the process for a minimal installation of NixOS on a Proxmox virtual machine. It leverages the =nixos-anywhere= tool for remote deployment and =disko= for declarative disk partitioning. It also covers the essential post-installation steps for integrating the new host with =sops-nix= for secrets management and lists available custom modules.
* TL;DR: Quick Install Guide * TL;DR: Quick Install Guide
1. *Prepare VM:* Boot the target Proxmox VM from a NixOS ISO and set a root password: 1. *Prepare VM:* Boot the target Proxmox VM from a NixOS ISO and set a root password:
@@ -38,6 +38,8 @@ This guide documents the process for a minimal installation of NixOS on a Proxmo
- [[#post-installation-secrets-management][Post-Installation: Secrets Management]] - [[#post-installation-secrets-management][Post-Installation: Secrets Management]]
- [[#step-1-generating-the-host-age-key][Step 1: Generating the Host AGE Key]] - [[#step-1-generating-the-host-age-key][Step 1: Generating the Host AGE Key]]
- [[#step-2-updating-sops-and-re-encrypting-secrets][Step 2: Updating SOPS and Re-encrypting Secrets]] - [[#step-2-updating-sops-and-re-encrypting-secrets][Step 2: Updating SOPS and Re-encrypting Secrets]]
- [[#optional-nixos-modules][Optional NixOS Modules]]
- [[#reverse-proxies][Reverse Proxies]]
- [[#notes-and-configuration-details][Notes and Configuration Details]] - [[#notes-and-configuration-details][Notes and Configuration Details]]
- [[#disko-configuration-for-proxmox-mbr-boot][Disko Configuration for Proxmox (MBR Boot)]] - [[#disko-configuration-for-proxmox-mbr-boot][Disko Configuration for Proxmox (MBR Boot)]]
- [[#generating-hardware-configuration][Generating Hardware Configuration]] - [[#generating-hardware-configuration][Generating Hardware Configuration]]
@@ -102,12 +104,50 @@ The new AGE key must be added to your =.sops.yaml= configuration file. This allo
- *susano - *susano
#+end_src #+end_src
3. After saving the updated =.sops.yaml= file, run the =updatekeys= command. This re-encrypts the specified secrets file with the new set of keys defined in =.sops.yaml=. For more information, see the [[https://github.com/getsops/sops?tab=readme-ov-file#281updatekeys-command][official documentation]]. 3. After saving the updated =.sops.yaml= file, run the =updatekeys= command. This re-encrypts the specified secrets file with the new set of keys defined in =.sots.yaml=. For more information, see the [[https://github.com/getsops/sops?tab=readme-ov-file#281updatekeys-command][official documentation]].
#+begin_src sh #+begin_src sh
sops updatekeys secrets/secrets.yaml sops updatekeys secrets/secrets.yaml
#+end_src #+end_src
Your secrets are now encrypted for both the primary key and the new host's key. Your secrets are now encrypted for both the primary key and the new host's key.
* Optional NixOS Modules
** Reverse Proxies
The following modules can be enabled to provide a reverse proxy.
*Note:* Currently, none of the available reverse proxy modules (NGINX, Traefik, Caddy) are functional. This is a known issue that needs to be investigated.
*** NGINX
The initial switch to this configuration may be slow as it waits for ACME to issue SSL certificates.
#+begin_src nix
dov = {
# Reverse Proxy
reverse-proxy = {
nginx.enable = true;
};
};
#+end_src
*** Traefik
This module is currently considered to be in a broken state.
#+begin_src nix
dov = {
# Reverse Proxy
reverse-proxy = {
traefik.enable = true;
};
};
#+end_src
*** Caddy
#+begin_src nix
dov = {
# Reverse Proxy
reverse-proxy = {
caddy.enable = true;
};
};
#+end_src
* Notes and Configuration Details * Notes and Configuration Details
** Disko Configuration for Proxmox (MBR Boot) ** Disko Configuration for Proxmox (MBR Boot)
A critical requirement for ensuring a NixOS VM can boot correctly in Proxmox is the disk partition scheme. Proxmox expects a Master Boot Record (MBR) compatible setup. A critical requirement for ensuring a NixOS VM can boot correctly in Proxmox is the disk partition scheme. Proxmox expects a Master Boot Record (MBR) compatible setup.
@@ -154,5 +194,9 @@ nix run github:nix-community/nixos-anywhere -- \
#+end_src #+end_src
* TODOs * TODOs
- [ ] Refactor the =disko= configuration to make the disk device name (e.g., =/dev/sda=) a variable. This will avoid hardcoding the value and make the configuration more portable across different hardware setups. - [ ] Investigate and fix the issue preventing any of the reverse proxy modules (NGINX, Traefik, Caddy) from working correctly.
- [ ] Troubleshoot and fix an issue that occurs when reloading the NixOS configuration remotely, which breaks the SSH pipe and requires entering the root password three times.
- [ ] Investigate and resolve the issue where updating a user's password declaratively using a secret managed by =sops= failed after the initial installation. - [ ] Investigate and resolve the issue where updating a user's password declaratively using a secret managed by =sops= failed after the initial installation.
- [ ] Refactor the =disko= configuration to make the disk device name (e.g., =/dev/sda=) a variable. This will avoid hardcoding the value and make the configuration more portable.
- [ ] Create a custom ISO image to streamline the installation process, potentially pre-configuring items like the root user to avoid manual console steps.
- [ ] Develop an automated installation script to handle the post-install process, such as fetching the AGE key and updating sops, based on [[https://unmovedcentre.com/posts/remote-install-nixos-config/#update-sops-file][this guide]].
+1
View File
@@ -51,6 +51,7 @@
sops-nix.nixosModules.sops sops-nix.nixosModules.sops
./main ./main
./modules
]; ];
}; };
}; };
+1 -1
View File
@@ -1,6 +1,6 @@
keys: keys:
- &primary age19wvqtn4ju6k4vs8fxr34unl6xx4cv04jw0lx9ps20xlde927zfssgl4qke - &primary age19wvqtn4ju6k4vs8fxr34unl6xx4cv04jw0lx9ps20xlde927zfssgl4qke
- &susano age1puzhjqxkxxfygm00taqql9vsv26cn2drqr3fk097mnu6t90fn9rqx7vtvs - &susano age1z2982ful2wun2ec3vjn2q22yzmrrk72t7qhlhtkwh8vfnrrfrdpqhc8xs0
creation_rules: creation_rules:
- path_regex: secrets/secrets.yaml$ - path_regex: secrets/secrets.yaml$
key_groups: key_groups:
+18
View File
@@ -129,6 +129,24 @@ in {
}; };
}; };
###
# My Services
###
dov = {
# Reverse Proxy
reverse-proxy = {
nginx.enable = false; # TODO does not work for some reason
traefik.enable = false; # TODO has issues retrieving certificate from duckdns
caddy.enable = false; # TODO has issues retrieving certificate from duckdns
};
virtualisation = {
podman.enable = false;
docker.enable = true;
};
};
# DO NOT CHANGE AT ANY POINT! # DO NOT CHANGE AT ANY POINT!
system.stateVersion = "25.05"; system.stateVersion = "25.05";
} }
+14 -13
View File
@@ -9,27 +9,28 @@ example_booleans:
- ENC[AES256_GCM,data:4rh2xA==,iv:2wQtaVPzLjQzPezrxd1w4/IZu4bT0rvU8G/edcsQ7VQ=,tag:re5rdTqPNSTZ+CuZjvs86A==,type:bool] - ENC[AES256_GCM,data:4rh2xA==,iv:2wQtaVPzLjQzPezrxd1w4/IZu4bT0rvU8G/edcsQ7VQ=,tag:re5rdTqPNSTZ+CuZjvs86A==,type:bool]
- ENC[AES256_GCM,data:5VhbnIk=,iv:sRnE8roVMQVs1Dk9tOtALWiDtfM4aJiSX5gb/MDHak8=,tag:egUULcUP5vCsy5uUM+j6dA==,type:bool] - ENC[AES256_GCM,data:5VhbnIk=,iv:sRnE8roVMQVs1Dk9tOtALWiDtfM4aJiSX5gb/MDHak8=,tag:egUULcUP5vCsy5uUM+j6dA==,type:bool]
user_password: ENC[AES256_GCM,data:Q7rk67ylyjr5Sa+AYCxnQAPLbBP5Fy85wTGLZuqxBG3iJ+MmhEgfeatVA2tcsY7GSaU/vghny+TJtrvhDYYMqa10h/F0wPxUjId78qkhKbnRQs4mqAxA9heSi4ojp1kh/pXN7tj64wNyJA==,iv:FTUojVNz78tn/Uj1N8Oj5Iov9eEMRo5vz+mqHdewxjg=,tag:YF74hLXXUby0IjHrqdkBUQ==,type:str] user_password: ENC[AES256_GCM,data:Q7rk67ylyjr5Sa+AYCxnQAPLbBP5Fy85wTGLZuqxBG3iJ+MmhEgfeatVA2tcsY7GSaU/vghny+TJtrvhDYYMqa10h/F0wPxUjId78qkhKbnRQs4mqAxA9heSi4ojp1kh/pXN7tj64wNyJA==,iv:FTUojVNz78tn/Uj1N8Oj5Iov9eEMRo5vz+mqHdewxjg=,tag:YF74hLXXUby0IjHrqdkBUQ==,type:str]
duckdns-token: ENC[AES256_GCM,data:Gf3kIpOO/X+ZVXV4w71Fp5qMuNedBBoobazAFpp22RC70xKb6xsJVffWdtFq0blDe5Y=,iv:SNq6wnhG6CuDwB3NQ/PryTgY3U/J2g1XfGCW7gSEYbo=,tag:MWqhrJRreGZ/SaapAaCXQA==,type:str]
sops: sops:
age: age:
- recipient: age19wvqtn4ju6k4vs8fxr34unl6xx4cv04jw0lx9ps20xlde927zfssgl4qke - recipient: age19wvqtn4ju6k4vs8fxr34unl6xx4cv04jw0lx9ps20xlde927zfssgl4qke
enc: | enc: |
-----BEGIN AGE ENCRYPTED FILE----- -----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBHbE12b2ZsU2VNSjVwR29M YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBQVlphbjR3YXMxSUM4bElk
WU1ZT2gwUHo3eXE3a1EzRDVrb2g1V2sybTFvCmp6dmlKZjdxM1ZiZUdrZ2ZZaXNz bmtoWGhCc0FEQkZJSHhvVW5yNHEybzBPSVFrCjU4TG4yUTNKaTBmVVdxc1dYT2tQ
ZWRHNmVwVUhUcWJoYVluOXN2aWpSVEUKLS0tIDVHaVhob0J6RlFhb1pvOG5OZy9W YUpWT0RvZFhXZWJCNDNVVFZndFVsblkKLS0tIER5YkorbnBXMDJJbWgyUGVaSUhE
UjRFMDhvOElxc3U2OGZjOFp2aFdodWsKIJFb3ZUuLDAgCel09B8fdpowa+A8R/HT SDNXdkIwWnFSWTVoMjBqdnhVZmVhTU0KSx0FqiVZX0B5gapQS/SlxqRk1sTMAR1e
vq4aS7TFAo4GsTfm6oF7AejnRj8teqqBTD99coQZeRJc8C6J+hp9FQ== SiKifKlQ0vJIw8X3sLmlomHL+7C7SUmnI1gxFTxYQeHH7IRiFJhnOQ==
-----END AGE ENCRYPTED FILE----- -----END AGE ENCRYPTED FILE-----
- recipient: age1puzhjqxkxxfygm00taqql9vsv26cn2drqr3fk097mnu6t90fn9rqx7vtvs - recipient: age1z2982ful2wun2ec3vjn2q22yzmrrk72t7qhlhtkwh8vfnrrfrdpqhc8xs0
enc: | enc: |
-----BEGIN AGE ENCRYPTED FILE----- -----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBXaDJoMEphNjBjcFBVdVJ1 YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBCRlQvTktUUHZyTThtMi9w
VGJjU1VtRmhtL00zUGJaRDMxRHMyS2x1ekJBCjA1Z3V6YzhHaTNZd2FxZmoybjgr OTB5UUZFMkYweWxaNS9sNjdNWmtvbUFrMGc4ClhqWUdVRFRkRWNacGJnQTdnQ3I1
S3dWUDM0Ty9ZZkV3RFhjRWRnVURJeHMKLS0tIFM5VTVuSFdOQnFBby9OQWJWZ0pt dGxkRVVzTG1PaDlETC8vcDNGOFgxaDgKLS0tIGJSY0VZbzZnZVRhVWgvZ25DRjg3
N0Q0a2J2WURqZlRadUdacGdHZUUrQWMK2Q1nwOWsGSzlcuZfcnq/P/v4i3nriUGY NHdWQnlGbk43WS80VDkxV0o4TE5uSUUK0WSdFzR3u0pLUYHXaTMrtBm0sKKe9ZPG
l9izT0xS6M8cHoh10YK3Qe1LcxfT/v0pXD8ppARdEDbEcJahb5ZHiA== nF90b/jv66WGIH1n2oFaaohCkd7DZGzSpr0+KsqX6pkszYnp39YC5A==
-----END AGE ENCRYPTED FILE----- -----END AGE ENCRYPTED FILE-----
lastmodified: "2025-07-26T12:25:08Z" lastmodified: "2025-07-26T13:53:03Z"
mac: ENC[AES256_GCM,data:eE/qjURtZPxwGpzvb0C64nZHaLSWL26kTU+fhx7dv+T84pCLtDwSiLrUfR33aSPtu9xTFJIiiFPQCiew99UXMsGKt+CVVGtR7frS5DktvABIfHuPznP3q6ykYMrNplK+xNQx0n6cZ/BNRVgc/kMPB9J3QudglAuxP3rMHrcjubA=,iv:okgPMpyGw0bUCEc/XrKonK5EUYrJjNRkAF/0t7TyoZw=,tag:fBQhqIASwsrl6//mXYcBzQ==,type:str] mac: ENC[AES256_GCM,data:WJJxd7d/Ld3z54JMgB7RhiBzy1P/hW14kRjfpX4pRIKzNzvUEivh1FQ1NUbonAGXrZZhE0WNPQaLcv185KeqXLF3NxWTawH+he+/uZr+cqcLU8Ylnyt4sbDDUCJgfo8HU0d+7xWrXblNqWQDHcEvm+KoSgwFYfBVGGvpCOv/mIs=,iv:jRMxA37VB21CQ1DqtKGYAMBHkf1O6bi65fvB0yh7roU=,tag:k29jd2jP137EkemkE4p2fw==,type:str]
unencrypted_suffix: _unencrypted unencrypted_suffix: _unencrypted
version: 3.10.2 version: 3.10.2
+2 -6
View File
@@ -5,9 +5,7 @@
defaultSopsFile = ./secrets/secrets.yaml; defaultSopsFile = ./secrets/secrets.yaml;
age = { age = {
# This will automatically import SSH keys as age keys # This will automatically import SSH keys as age keys
sshKeyPaths = [ sshKeyPaths = [ "/etc/ssh/ssh_host_ed25519_key" ];
"/etc/ssh/ssh_host_ed25519_key"
];
# This is using an age key that is expected to already be in the filesystem # This is using an age key that is expected to already be in the filesystem
keyFile = "/var/lib/sops-nix/key.txt"; keyFile = "/var/lib/sops-nix/key.txt";
# This will generate a new key if the key specified above does not exist # This will generate a new key if the key specified above does not exist
@@ -15,8 +13,6 @@
# This is the actual specification of the secrets. # This is the actual specification of the secrets.
}; };
secrets."user_password" = { secrets = { "user_password" = { neededForUsers = true; }; };
neededForUsers = true;
};
}; };
} }
+8
View File
@@ -0,0 +1,8 @@
{ config, lib, pkgs, ... }:
{
imports = [
./reverse-proxy
./virtualisation
];
}
+62
View File
@@ -0,0 +1,62 @@
{ config, lib, pkgs, ... }:
with lib;
let
cfg = config.dov.reverse-proxy.caddy;
caddyWithDuckDNS = pkgs.caddy.withPlugins {
plugins = [
"github.com/caddy-dns/duckdns@v0.5.0"
];
# Replace with the hash NixOS provides on the first build attempt.
hash = "sha256-83ETc9K4T13Ws8gVOYwLarhuCA48Drs/i3rVLBMHyrc=";
};
email = "susano@local.com";
in {
options.dov.reverse-proxy.caddy = { enable = mkEnableOption "caddy config"; };
config = mkIf cfg.enable {
sops.secrets.duckdns-token = {
owner = config.services.caddy.user;
group = config.services.caddy.group;
};
services.caddy = {
enable = cfg.enable;
package = caddyWithDuckDNS;
environmentFile = config.sops.secrets.duckdns-token.path;
# Add a global options block.
# Let's Encrypt will use this email to send you important notices.
globalConfig = ''
email ${email}
'';
virtualHosts."test.susano-lab.duckdns.org" = {
extraConfig = ''
# Reverse proxy to your Immich instance.
reverse_proxy http://192.168.1.57:2283 {
# Send correct headers to the backend service.
header_up Host {host}
header_up X-Real-IP {remote_ip}
header_up X-Forwarded-For {remote_ip}
header_up X-Forwarded-Proto {scheme}
# Recommended for large file uploads in Immich.
transport http {
read_buffer 1m
}
}
# Configure automatic HTTPS with the DuckDNS provider.
tls {
dns duckdns {env.DUCKDNS_TOKEN}
propagation_timeout -1
}
'';
};
};
};
}
+9
View File
@@ -0,0 +1,9 @@
{ config, lib, pkgs, ... }:
{
imports = [
./nginx
./traefik
./caddy
];
}
+150
View File
@@ -0,0 +1,150 @@
{ config, lib, pkgs, ... }:
with lib;
let
cfg = config.dov.reverse-proxy.nginx;
email = "susano@local.com";
# configFile = pkgs.writeText "duckdns_config"
# ''
# example config file bla bla
# '';
in {
options.dov.reverse-proxy.nginx = { enable = mkEnableOption "nginx config"; };
config = mkIf cfg.enable {
sops.secrets.duckdns-token = {
owner = config.services.nginx.user;
group = config.services.nginx.group;
};
# 1. Enable Nginx
# 2. Enable Automatic Certificate Management (ACME)
# NixOS uses acme.sh to handle Let's Encrypt certificates.
security.acme = {
acceptTerms = true;
defaults.email = email;
# Define the certificates you want to obtain.
# We use the DNS-01 challenge for wildcard domains.
certs = {
# Certificate for *.susano-lab.duckdns.org
"susano-lab.duckdns.org" = {
domain = "*.susano-lab.duckdns.org";
extraDomainNames = [ "susano-lab.duckdns.org" ];
dnsProvider = "duckdns";
# The credentialsFile points to the secret file we created.
credentialsFile = config.sops.secrets.duckdns-token.path;
group = config.services.nginx.group;
};
# Certificate for *.susano-tailscale.duckdns.org
"susano-tailscale.duckdns.org" = {
domain = "*.susano-tailscale.duckdns.org";
extraDomainNames = [ "susano-tailscale.duckdns.org" ];
dnsProvider = "duckdns";
credentialsFile = config.sops.secrets.duckdns-token.path;
group = config.services.nginx.group;
};
};
};
# 3. Define Nginx Reverse Proxy Configuration
services.nginx = {
enable = cfg.enable;
# Use httpConfig to define 'map' blocks at the correct level.
httpConfig = ''
# Map for susano-lab.duckdns.org domains
map $host $lab_proxy_pass {
"immich.susano-lab.duckdns.org" "http://192.168.1.57:2283";
"jellyfin.susano-lab.duckdns.org" "http://192.168.1.64:8096";
"jellyseer.susano-lab.duckdns.org" "http://192.168.1.68:5055";
"nginx.susano-lab.duckdns.org" "http://192.168.1.57:81";
"portainer.susano-lab.duckdns.org" "https://192.168.1.57:9443";
"qbittorrent.susano-lab.duckdns.org" "http://192.168.1.57:8080";
"radarr.susano-lab.duckdns.org" "http://192.168.1.57:7878";
"searxng.susano-lab.duckdns.org" "http://192.168.1.82:8080";
"sonarr.susano-lab.duckdns.org" "http://192.168.1.57:8989";
"susano-lab.duckdns.org" "http://192.168.1.53:8006";
default "http://127.0.0.1:8000"; # Optional: a default to avoid errors if no host matches
}
# Map for susano-tailscale.duckdns.org domains
map $host $tailscale_proxy_pass {
"immich.susano-tailscale.duckdns.org" "http://192.168.1.57:2283";
"searxng.susano-tailscale.duckdns.org" "http://192.168.1.82:8080";
default "http://127.0.0.1:8000"; # Optional: a default
}
# Map for susano-traefik.duckdns.org domains
map $host $traefik_proxy_pass {
"immich.susano-traefik.duckdns.org" "http://192.168.1.57:2283";
default "http://127.0.0.1:8000"; # Optional: a default
}
'';
virtualHosts = {
# === Group for susano-lab.duckdns.org subdomains ===
"susano-lab" = {
serverName = "susano-lab.duckdns.org";
serverAliases = [
"test.susano-lab.duckdns.org"
"immich.susano-lab.duckdns.org"
"jellyfin.susano-lab.duckdns.org"
"jellyseer.susano-lab.duckdns.org"
"nginx.susano-lab.duckdns.org"
"portainer.susano-lab.duckdns.org"
"qbittorrent.susano-lab.duckdns.org"
"radarr.susano-lab.duckdns.org"
"searxng.susano-lab.duckdns.org"
"sonarr.susano-lab.duckdns.org"
];
useACMEHost = "susano-lab.duckdns.org";
forceSSL = true;
locations."/".extraConfig = ''
# The map block is removed from here.
# We now use the variable defined in httpConfig.
proxy_pass $lab_proxy_pass;
# Standard proxy headers for websockets and correct IP forwarding.
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
'';
};
# === Group for susano-tailscale.duckdns.org subdomains ===
"susano-tailscale" = {
serverName = "susano-tailscale.duckdns.org";
serverAliases = [
"immich.susano-tailscale.duckdns.org"
"searxng.susano-tailscale.duckdns.org"
];
useACMEHost = "susano-tailscale.duckdns.org";
forceSSL = true;
locations."/".extraConfig = ''
# Use the second variable defined in httpConfig.
proxy_pass $tailscale_proxy_pass;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
'';
};
};
};
};
}
+114
View File
@@ -0,0 +1,114 @@
{ config, lib, pkgs, ... }:
with lib;
let
cfg = config.dov.reverse-proxy.traefik;
in {
options.dov.reverse-proxy.traefik = { enable = mkEnableOption "traefik config"; };
config = mkIf cfg.enable {
# 1. SOPS Configuration for the DuckDNS Token
# This decrypts the secret and provides it to the Traefik service.
sops.secrets.duckdns-token = {
# The Traefik service needs permission to read this file.
owner = "traefik";
group = config.services.traefik.group;
};
# 3. Traefik Service Configuration
services.traefik = {
enable = true;
# Load the DuckDNS token as an environment variable for Traefik.
environmentFiles = [ config.sops.secrets.duckdns-token.path ];
# Static configuration (traefik.yml) - defines entrypoints and certificate resolvers.
staticConfigOptions = {
# -- EntryPoints: Where Traefik listens for traffic --
entryPoints = {
# Unsecured HTTP on port 80, mainly for redirection
web = {
address = ":80";
# Redirect all traffic from this entrypoint to the 'websecure' (HTTPS) entrypoint
http.redirections.entryPoint = {
to = "websecure";
scheme = "https";
permanent = true;
};
};
# Secured HTTPS on port 443
websecure = {
address = ":443";
};
};
# -- Certificate Resolver: How Traefik gets SSL certs --
certificatesResolvers = {
# We'll name our resolver 'duckdns'
duckdns = {
acme = {
email = "susano@local.com";
storage = "/var/lib/traefik/acme.json"; # Where Traefik stores certs
# Use the DNS-01 challenge with the DuckDNS provider
dnsChallenge = {
provider = "duckdns";
# Traefik will get the DUCKDNS_TOKEN from the environment file.
resolvers = [
"1.1.1.1:53"
"8.8.8.8:53"
];
};
};
};
};
# Optional but recommended: Enable the Traefik Dashboard
api.dashboard = true;
};
# Dynamic configuration - defines the actual routers and services.
dynamicConfigOptions = {
http = {
routers = {
# --- Router for the Traefik dashboard (optional) ---
dashboard-router = {
rule = "Host(`traefik.local.susano-traefik.duckdns.org`)"; # Example: A local-only subdomain
entryPoints = [ "websecure" ];
service = "api@internal"; # Special service for the dashboard
tls.certResolver = "duckdns";
};
immich-router = {
rule = "Host(`immich.susano-traefik.duckdns.org`)"; # 1. The new domain
entryPoints = [ "websecure" ]; # 2. Listen on HTTPS
service = "immich-service"; # 3. Link to the new Immich service
tls.certResolver = "duckdns"; # 4. Use the same SSL resolver
};
};
services = {
immich-service = {
loadBalancer.servers = [
# The backend URL for Immich
{ url = "http://192.168.1.57:2283"; }
];
};
};
middlewares = {
# --- Middleware for dashboard authentication (optional) ---
auth = {
# Run `nix-shell -p apacheHttpdTools --run "htpasswd -nb your-user your-password"`
# to generate the user:password hash.
basicAuth.users = [
"your-user:$apr1$....some-hash-here...."
];
};
};
};
};
};
};
}
+8
View File
@@ -0,0 +1,8 @@
{ config, lib, pkgs, ... }:
{
imports = [
./podman
./docker
];
}
+26
View File
@@ -0,0 +1,26 @@
{ config, lib, pkgs, ... }:
with lib;
let
cfg = config.dov.virtualisation.docker;
username = "susano";
in {
options.dov.virtualisation.docker = { enable = mkEnableOption "docker config"; };
config = mkIf cfg.enable {
users.extraGroups.docker.members = [ username ];
virtualisation.docker = {
enable = true;
rootless = {
enable = true;
setSocketVariable = true;
};
# TODO use if disko is btrfs
storageDriver = "btrfs";
};
};
}
+34
View File
@@ -0,0 +1,34 @@
{ config, lib, pkgs, ... }:
with lib;
let
cfg = config.dov.virtualisation.podman;
in {
options.dov.virtualisation.podman = { enable = mkEnableOption "podman config"; };
config = mkIf cfg.enable {
# Enable common container config files in /etc/containers
virtualisation.containers.enable = true;
virtualisation = {
podman = {
enable = true;
# Create a `docker` alias for podman, to use it as a drop-in replacement
dockerCompat = true;
# Required for containers under podman-compose to be able to talk to each other.
defaultNetwork.settings.dns_enabled = true;
};
};
# Useful other development tools
environment.systemPackages = with pkgs; [
dive # look into docker image layers
podman-tui # status of containers in the terminal
#docker-compose # start group of containers for dev
podman-compose # start group of containers for dev
];
};
}