Add nginx,traefik,caddy,docker,podman
This commit is contained in:
+47
-3
@@ -4,7 +4,7 @@
|
||||
#+OPTIONS: toc:t num:nil
|
||||
|
||||
*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
|
||||
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]]
|
||||
- [[#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]]
|
||||
- [[#optional-nixos-modules][Optional NixOS Modules]]
|
||||
- [[#reverse-proxies][Reverse Proxies]]
|
||||
- [[#notes-and-configuration-details][Notes and Configuration Details]]
|
||||
- [[#disko-configuration-for-proxmox-mbr-boot][Disko Configuration for Proxmox (MBR Boot)]]
|
||||
- [[#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
|
||||
#+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
|
||||
sops updatekeys secrets/secrets.yaml
|
||||
#+end_src
|
||||
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
|
||||
** 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.
|
||||
@@ -154,5 +194,9 @@ nix run github:nix-community/nixos-anywhere -- \
|
||||
#+end_src
|
||||
|
||||
* 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.
|
||||
- [ ] 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
-1
@@ -1,6 +1,6 @@
|
||||
keys:
|
||||
- &primary age19wvqtn4ju6k4vs8fxr34unl6xx4cv04jw0lx9ps20xlde927zfssgl4qke
|
||||
- &susano age1puzhjqxkxxfygm00taqql9vsv26cn2drqr3fk097mnu6t90fn9rqx7vtvs
|
||||
- &susano age1z2982ful2wun2ec3vjn2q22yzmrrk72t7qhlhtkwh8vfnrrfrdpqhc8xs0
|
||||
creation_rules:
|
||||
- path_regex: secrets/secrets.yaml$
|
||||
key_groups:
|
||||
|
||||
@@ -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!
|
||||
system.stateVersion = "25.05";
|
||||
}
|
||||
|
||||
+14
-13
@@ -9,27 +9,28 @@ example_booleans:
|
||||
- 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]
|
||||
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:
|
||||
age:
|
||||
- recipient: age19wvqtn4ju6k4vs8fxr34unl6xx4cv04jw0lx9ps20xlde927zfssgl4qke
|
||||
enc: |
|
||||
-----BEGIN AGE ENCRYPTED FILE-----
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBHbE12b2ZsU2VNSjVwR29M
|
||||
WU1ZT2gwUHo3eXE3a1EzRDVrb2g1V2sybTFvCmp6dmlKZjdxM1ZiZUdrZ2ZZaXNz
|
||||
ZWRHNmVwVUhUcWJoYVluOXN2aWpSVEUKLS0tIDVHaVhob0J6RlFhb1pvOG5OZy9W
|
||||
UjRFMDhvOElxc3U2OGZjOFp2aFdodWsKIJFb3ZUuLDAgCel09B8fdpowa+A8R/HT
|
||||
vq4aS7TFAo4GsTfm6oF7AejnRj8teqqBTD99coQZeRJc8C6J+hp9FQ==
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBQVlphbjR3YXMxSUM4bElk
|
||||
bmtoWGhCc0FEQkZJSHhvVW5yNHEybzBPSVFrCjU4TG4yUTNKaTBmVVdxc1dYT2tQ
|
||||
YUpWT0RvZFhXZWJCNDNVVFZndFVsblkKLS0tIER5YkorbnBXMDJJbWgyUGVaSUhE
|
||||
SDNXdkIwWnFSWTVoMjBqdnhVZmVhTU0KSx0FqiVZX0B5gapQS/SlxqRk1sTMAR1e
|
||||
SiKifKlQ0vJIw8X3sLmlomHL+7C7SUmnI1gxFTxYQeHH7IRiFJhnOQ==
|
||||
-----END AGE ENCRYPTED FILE-----
|
||||
- recipient: age1puzhjqxkxxfygm00taqql9vsv26cn2drqr3fk097mnu6t90fn9rqx7vtvs
|
||||
- recipient: age1z2982ful2wun2ec3vjn2q22yzmrrk72t7qhlhtkwh8vfnrrfrdpqhc8xs0
|
||||
enc: |
|
||||
-----BEGIN AGE ENCRYPTED FILE-----
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBXaDJoMEphNjBjcFBVdVJ1
|
||||
VGJjU1VtRmhtL00zUGJaRDMxRHMyS2x1ekJBCjA1Z3V6YzhHaTNZd2FxZmoybjgr
|
||||
S3dWUDM0Ty9ZZkV3RFhjRWRnVURJeHMKLS0tIFM5VTVuSFdOQnFBby9OQWJWZ0pt
|
||||
N0Q0a2J2WURqZlRadUdacGdHZUUrQWMK2Q1nwOWsGSzlcuZfcnq/P/v4i3nriUGY
|
||||
l9izT0xS6M8cHoh10YK3Qe1LcxfT/v0pXD8ppARdEDbEcJahb5ZHiA==
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBCRlQvTktUUHZyTThtMi9w
|
||||
OTB5UUZFMkYweWxaNS9sNjdNWmtvbUFrMGc4ClhqWUdVRFRkRWNacGJnQTdnQ3I1
|
||||
dGxkRVVzTG1PaDlETC8vcDNGOFgxaDgKLS0tIGJSY0VZbzZnZVRhVWgvZ25DRjg3
|
||||
NHdWQnlGbk43WS80VDkxV0o4TE5uSUUK0WSdFzR3u0pLUYHXaTMrtBm0sKKe9ZPG
|
||||
nF90b/jv66WGIH1n2oFaaohCkd7DZGzSpr0+KsqX6pkszYnp39YC5A==
|
||||
-----END AGE ENCRYPTED FILE-----
|
||||
lastmodified: "2025-07-26T12:25:08Z"
|
||||
mac: ENC[AES256_GCM,data:eE/qjURtZPxwGpzvb0C64nZHaLSWL26kTU+fhx7dv+T84pCLtDwSiLrUfR33aSPtu9xTFJIiiFPQCiew99UXMsGKt+CVVGtR7frS5DktvABIfHuPznP3q6ykYMrNplK+xNQx0n6cZ/BNRVgc/kMPB9J3QudglAuxP3rMHrcjubA=,iv:okgPMpyGw0bUCEc/XrKonK5EUYrJjNRkAF/0t7TyoZw=,tag:fBQhqIASwsrl6//mXYcBzQ==,type:str]
|
||||
lastmodified: "2025-07-26T13:53:03Z"
|
||||
mac: ENC[AES256_GCM,data:WJJxd7d/Ld3z54JMgB7RhiBzy1P/hW14kRjfpX4pRIKzNzvUEivh1FQ1NUbonAGXrZZhE0WNPQaLcv185KeqXLF3NxWTawH+he+/uZr+cqcLU8Ylnyt4sbDDUCJgfo8HU0d+7xWrXblNqWQDHcEvm+KoSgwFYfBVGGvpCOv/mIs=,iv:jRMxA37VB21CQ1DqtKGYAMBHkf1O6bi65fvB0yh7roU=,tag:k29jd2jP137EkemkE4p2fw==,type:str]
|
||||
unencrypted_suffix: _unencrypted
|
||||
version: 3.10.2
|
||||
|
||||
+2
-6
@@ -5,9 +5,7 @@
|
||||
defaultSopsFile = ./secrets/secrets.yaml;
|
||||
age = {
|
||||
# This will automatically import SSH keys as age keys
|
||||
sshKeyPaths = [
|
||||
"/etc/ssh/ssh_host_ed25519_key"
|
||||
];
|
||||
sshKeyPaths = [ "/etc/ssh/ssh_host_ed25519_key" ];
|
||||
# This is using an age key that is expected to already be in the filesystem
|
||||
keyFile = "/var/lib/sops-nix/key.txt";
|
||||
# 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.
|
||||
};
|
||||
|
||||
secrets."user_password" = {
|
||||
neededForUsers = true;
|
||||
};
|
||||
secrets = { "user_password" = { neededForUsers = true; }; };
|
||||
};
|
||||
}
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
{ config, lib, pkgs, ... }:
|
||||
|
||||
{
|
||||
imports = [
|
||||
./reverse-proxy
|
||||
./virtualisation
|
||||
];
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
'';
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{ config, lib, pkgs, ... }:
|
||||
|
||||
{
|
||||
imports = [
|
||||
./nginx
|
||||
./traefik
|
||||
./caddy
|
||||
];
|
||||
}
|
||||
@@ -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;
|
||||
'';
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -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...."
|
||||
];
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
{ config, lib, pkgs, ... }:
|
||||
|
||||
{
|
||||
imports = [
|
||||
./podman
|
||||
./docker
|
||||
];
|
||||
}
|
||||
@@ -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";
|
||||
};
|
||||
};
|
||||
|
||||
}
|
||||
@@ -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
|
||||
];
|
||||
};
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user