{ config, pkgs, ... }: let inherit (config.networking) domain; root_email = "contact@${domain}"; ptero_host = "ptero.${domain}"; DATA_DIR = "/var/lib/pterodactylpanel"; panel_user = "pterodactyl"; local_bridge = "ptero-local-br"; ptero_ver = "1.11.5"; ptero_port = "8042"; in { users.users.${panel_user} = { isSystemUser = true; extraGroups = [ "docker" ]; group = panel_user; }; users.groups.${panel_user} = { }; age.secrets.ptero_env = { file = ../secrets/ptero_env.age; }; systemd.services.init-ptero-data-dir = { description = "Create the pterodactyl panel data dir"; wantedBy = [ "multi-user.target" ]; serviceConfig.Type = "oneshot"; script = '' mkdir -p ${DATA_DIR}/database mkdir -p ${DATA_DIR}/cache mkdir -p ${DATA_DIR}/panel/var mkdir -p ${DATA_DIR}/panel/logs mkdir -p ${DATA_DIR}/panel/nginx chown ${panel_user}:${panel_user} -R ${DATA_DIR} chmod +777 -R ${DATA_DIR} ''; }; virtualisation.oci-containers.backend = "podman"; systemd.services.init-ptero-local-network = { description = "Create the network bridge ${local_bridge} for ptero."; after = [ "network.target" ]; wantedBy = [ "multi-user.target" ]; serviceConfig.Type = "oneshot"; script = let podmancli = "${config.virtualisation.podman.package}/bin/podman"; in '' check=$(${podmancli} pod ls | grep "ptero" || true) if [ -z "$check" ]; then ${podmancli} pod create -p "${ptero_port}:80" ptero else echo "ptero pod already exists" fi ''; }; virtualisation.oci-containers.containers."ptero-mysql" = { image = "library/mysql:8.0"; workdir = "${DATA_DIR}/database"; extraOptions = [ "--pod=ptero" ]; environment = { "MYSQL_USER" = "pterodactyl"; "MYSQL_DATABASE" = "panel"; }; environmentFiles = [ config.age.secrets.ptero_env.path ]; volumes = [ "${DATA_DIR}/database:/var/lib/mysql" "${DATA_DIR}/database:${DATA_DIR}/database" ]; cmd = [ "--default-authentication-plugin=mysql_native_password" ]; }; virtualisation.oci-containers.containers."ptero-cache" = { image = "redis:alpine"; workdir = "${DATA_DIR}/cache"; volumes = [ "${DATA_DIR}/cache:${DATA_DIR}/cache" ]; extraOptions = [ "--pod=ptero" ]; }; virtualisation.oci-containers.containers."ptero-panel" = { image = "ghcr.io/pterodactyl/panel:v${ptero_ver}"; volumes = [ "${DATA_DIR}/panel/var/:/app/var/" "${DATA_DIR}/panel/logs/:/app/storage/logs" "${DATA_DIR}/panel/nginx/:/etc/nginx/conf.d/" ]; extraOptions = [ "--pod=ptero" ]; environment = { "APP_URL" = "https://${ptero_host}"; "APP_TIMEZONE" = "Europe/Berlin"; "APP_SERVICE_AUTHOR" = root_email; "MAIL_FROM" = "noreply@${domain}"; "MAIL_DRIVER" = "smtp"; "MAIL_HOST" = "mail"; "MAIL_PORT" = "25"; "MAIL_USERNAME" = ""; "MAIL_PASSWORD" = ""; "MAIL_ENCRYPTION" = "true"; "APP_ENV" = "production"; "APP_ENVIRONMENT_ONLY" = "false"; "CACHE_DRIVER" = "redis"; "SESSION_DRIVER" = "redis"; "QUEUE_DRIVER" = "redis"; "REDIS_HOST" = "127.0.0.1"; "DB_HOST" = "127.0.0.1"; "TRUSTED_PROXIES" = "*"; }; labels = { "traefik.http.routers.pterodactyl_panel.entrypoints" = "web"; }; environmentFiles = [ config.age.secrets.ptero_env.path ]; }; security.acme.certs."${domain}".extraDomainNames = [ ptero_host ]; services.nginx = { enable = true; virtualHosts."${ptero_host}" = { serverName = ptero_host; forceSSL = true; useACMEHost = domain; locations."/" = { proxyPass = "http://127.0.0.1:${ptero_port}"; }; }; }; environment.systemPackages = with pkgs; [ (writeShellScriptBin "ptero-nix" "${config.virtualisation.podman.package}/bin/podman exec -it ptero-panel php artisan $@") ]; }