This commit is contained in:
kalmenn 2025-07-07 13:52:45 +02:00
parent 6fbd9b2a6c
commit 816e55f41e
Signed by: kalmenn
GPG key ID: F500055C44BC3834
6 changed files with 290 additions and 76 deletions

1
.gitignore vendored Normal file
View file

@ -0,0 +1 @@
/result

View file

@ -11,9 +11,13 @@
);
packages = nixpkgs.lib.genAttrs [ "x86_64-linux" ] (
system: nixpkgs.lib.genAttrs [ "pterodactyl" "php" "wings" ] (
package: import ./packages/${package}.nix { pkgs = import nixpkgs { inherit system; }; };
)
});
system: let
pkgs = import nixpkgs { inherit system; };
in rec {
php = import ./packages/php.nix { inherit pkgs; };
pterodactyl = import ./packages/pterodactyl.nix { inherit pkgs php; };
wings = import ./packages/wings.nix { inherit pkgs; };
}
);
};
}

View file

@ -2,20 +2,36 @@
{ lib, config, pkgs, ... }:
let
cfg = config.services.pterodactyl;
flakePkgs = flake.outputs.packages.${pkgs.system};
thisConfig = config.services.pterodactyl;
defaultUser = "pterodactyl";
in {
options.services.pterodactyl = {
enable = lib.mkEnableOption "Enable the pterodacytl game server panel";
proxy = {
enable = lib.mkEnableOption "Automatically configure Nginx to serve the panel";
serverName = lib.mkOption {
pkg = lib.mkOption {
type = lib.types.package;
description = "The package in which to source the php files used by pterodactyl";
default = flake.outputs.packages.${pkgs.system}.pterodactyl;
};
dataDir = lib.mkOption {
type = lib.types.str;
description = "The canonical domain on which the panel will be hosted";
description = "The directory in which to store stateful data";
default = "/data/services/pterodactyl";
};
server = {
enable = lib.mkEnableOption ''
Automatically configure Nginx to serve the panel
If you need more control over the nginx proxy, for now you must leave this option disabled
and write your own nginx configuration. Feel free to copy ours as a starting point.
'';
name = lib.mkOption {
type = lib.types.str;
description = "The canonical domain name on which the panel will be hosted";
default = "localhost";
example = "pterodactyl.example.com";
};
@ -27,27 +43,46 @@ in {
};
};
php = {
socket = lib.mkOption {
type = lib.types.path;
description = "The location of the unix socket used by php-fpm";
default = config.services.phpfpm.pools.pterodactyl.socket;
};
user = lib.mkOption {
type = lib.types.str;
description = ''
The user that owns the files managed by the panel.
If you change this, make sure their files are accessible by your www user
The user that the php-fpm processes will run under. See also the `group` option.
If you change this, make sure the unix socket is accessible by your www user
(probably "nginx").
'';
default = defaultUser;
example = defaultUser;
};
group = lib.mkOption {
type = lib.types.str;
description = ''
The group that the php-fpm processes will run under. See also the `user` option.
If you change this, make sure the unix socket is accessible by your www user
(probably "nginx").
'';
default = defaultUser;
};
root = lib.mkOption {
type = lib.types.path;
description = "The main application directory";
default = "/srv/pterodactyl";
};
pkg = lib.mkOption {
type = lib.types.package;
description = "The package in which to source the php files used by pterodactyl";
default = flakePkgs.pterodactyl;
description = "The php package to use";
default = flake.outputs.packages.${pkgs.system}.php;
};
dataDir = lib.mkOption {
type = lib.types.str;
description = "The directory in which to store stateful data";
default = "/var/lib/pterodactyl";
};
redis = {
@ -55,42 +90,107 @@ in {
name = lib.mkOption {
type = lib.types.str;
description = "The Redis server name.";
description = "The Redis server name. Used when configuring locally";
default = "redis-pterodactyl";
};
port = lib.mkOption {
type = lib.types.port;
description = "The port the redis server listens on";
description = "The port Redis listens on";
default = 6379;
};
host = lib.mkOption {
type = lib.types.str;
description = "The address with which to reach Redis";
default = "127.0.0.1";
};
};
config = lib.mkIf cfg.enable {
users = lib.mkIf (cfg.user == defaultUser) {
users.${cfg.user} = {
database = {
configureLocally = lib.mkEnableOption "Configure a local MariaDB instance";
type = lib.mkOption {
type = lib.types.str;
description = ''
The type of database pterodactyl should connect to (supports "mariadb" and "mysql")
'';
default = "mariadb";
};
host = lib.mkOption {
type = lib.types.str;
description = "The address with which to reach the database";
default = "127.0.0.1";
};
port = lib.mkOption {
type = lib.types.port;
description = "The port the database listens on";
default = 3336;
};
name = lib.mkOption {
type = lib.types.str;
description = "The name of the database that you've created in mysql / mariadb";
default = "pterodactyl";
};
username = lib.mkOption {
type = lib.types.str;
description = "The name of the user that you've created in mysql / mariadb";
default = thisConfig.php.user;
};
};
};
config = lib.mkIf thisConfig.enable {
users = {
users = {
${thisConfig.php.user} = lib.mkIf (thisConfig.php.user == defaultUser) {
isSystemUser = true;
createHome = true;
home = cfg.dataDir;
group = cfg.user;
home = "/var/lib/pterodactyl";
group = thisConfig.php.group;
};
groups.${cfg.user} = { };
users.${config.services.nginx.user}.extraGroups = [ cfg.user ];
${config.services.nginx.user} = lib.mkIf (thisConfig.php.group == defaultUser) {
extraGroups = [ thisConfig.php.group ];
};
};
services.redis.servers.${cfg.redis.name} = lib.mkIf cfg.redis.configureLocally {
groups = {
${thisConfig.php.group} = lib.mkIf (thisConfig.php.group == defaultUser) {};
};
};
services.redis.servers.${thisConfig.redis.name} = lib.mkIf thisConfig.redis.configureLocally {
enable = true;
port = cfg.redis.port;
port = thisConfig.redis.port;
};
services.nginx = lib.mkIf cfg.proxy.enable {
services.mysql = lib.mkIf thisConfig.database.configureLocally {
enable = true;
package = pkgs.mariadb;
ensureDatabases = [ thisConfig.database.name ];
ensureUsers = [
{
name = thisConfig.database.name;
ensurePermissions = {
"${thisConfig.database.name}.*" = "ALL PRIVILEGES";
};
}
];
};
services.nginx = lib.mkIf thisConfig.server.enable {
enable = true;
virtualHosts."${cfg.serverName}" = {
root = "${config.services.pterodactyl.pkg}/public";
virtualHosts."pterodactyl" = {
serverName = thisConfig.server.name;
root = "${thisConfig.php.root}/public";
extraConfig = ''
index index.html index.htm index.php;
@ -101,7 +201,7 @@ in {
include ${pkgs.nginx}/conf/fastcgi_params;
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass unix:${config.services.phpfpm.pools.pterodactyl.socket};
fastcgi_pass unix:${thisConfig.php.socket};
fastcgi_index index.php;
@ -127,7 +227,7 @@ in {
};
services.phpfpm.pools.pterodactyl = {
user = cfg.user;
user = thisConfig.php.user;
settings = {
"listen.owner" = config.services.nginx.user;
"pm" = "dynamic";
@ -143,23 +243,125 @@ in {
"php_admin_value[error_log]" = "stderr";
"php_admin_flag[daemonize]" = "false";
};
phpEnv = {
APP_ENV = "production";
APP_DEBUG = "false";
# APP_KEY = ""; # TODO: Figure how to make this persistent but automatically generated
APP_THEME = "pterodactyl"; # TODO: Configure
APP_TIMEZONE = "Europe/Paris"; # TODO: Configure
APP_URL = "https://${thisConfig.server.name}"; # TODO: create another config option for this
APP_LOCALE = "en"; # TODO: Configure
APP_ENVIRONMENT_ONLY = "true";
LOG_CHANNEL = "daily";
LOG_DEPRECATIONS_CHANNEL = "null";
LOG_LEVEL = "debug";
DB_CONNECTION = thisConfig.database.type;
DB_HOST = thisConfig.database.host;
DB_PORT = builtins.toString thisConfig.database.port;
DB_DATABASE = thisConfig.database.name;
DB_USERNAME = thisConfig.database.username;
DB_PASSWORD = ""; # TODO: allow a password
REDIS_HOST = thisConfig.redis.host;
REDIS_PASSWORD = ""; # TODO: allow a password
REDIS_PORT = builtins.toString thisConfig.redis.port;
# TODO: Configure
CACHE_DRIVER = "redis";
QUEUE_CONNECTION = "redis";
SESSION_DRIVER = "redis";
HASHIDS_SALT = "Tbl~GYn;t6s'iKCDMe7u"; # TODO: configure
HASHIDS_LENGTH = "8";
# MAIL_MAILER = "smtp";
# MAIL_HOST = "smtp.example.com";
# MAIL_PORT = "25";
# MAIL_USERNAME = "";
# MAIL_PASSWORD = "";
# MAIL_ENCRYPTION = "tls";
# MAIL_FROM_ADDRESS = "no-reply@example.com";
# MAIL_FROM_NAME="Pterodactyl Panel"
# You should set this to your domain to prevent it defaulting to 'localhost', causing
# mail servers such as Gmail to reject your mail.
#
# @see: https://github.com/pterodactyl/panel/pull/3110
# MAIL_EHLO_DOMAIN=panel.example.com
};
};
systemd.services.pteroq = {
systemd = {
tmpfiles.settings."10-pterodactyl" = let
user = thisConfig.php.user;
group = thisConfig.php.group;
in {
"${thisConfig.dataDir}/cache" = { "d" = { inherit group user; mode = "750"; }; };
"${thisConfig.dataDir}/storage" = { "d" = { inherit group user; mode = "750"; }; };
"${thisConfig.php.root}" = { "d" = { inherit group user; mode = "750"; }; };
};
services = {
pterodactyl-setup = {
description = "Setup the necessary files for the pterodactyl panel to work";
before = [ "pteroq.service" ]
++ (if thisConfig.server.enable then [ "nginx.service" ] else []);
wantedBy = [ "multi-user.target" "pteroq.service" ]
++ (if thisConfig.server.enable then [ "nginx.service" ] else []);
serviceConfig = {
Type = "oneshot";
User = thisConfig.php.user;
Group = thisConfig.php.group;
ExecStart = pkgs.writeShellScript "pterodactyl-setup.sh" ''
SRC="${thisConfig.pkg}/share/php/pterodactyl-panel/";
DEST="${thisConfig.php.root}";
USER="${thisConfig.php.user}";
GROUP="${thisConfig.php.group}";
rm $DEST/* -r;
for file in app config database public resources routes vendor artisan; do
cp -r "$SRC/$file" "$DEST"
chmod -R 0550 "$DEST/$file"
done;
mkdir -p -m 0750 "$DEST/bootstrap"
cp "$SRC/bootstrap/app.php" "$DEST/bootstrap/app.php";
chmod 0550 "$DEST/bootstrap/app.php";
ln -s "${thisConfig.dataDir}/cache" "$DEST/bootstrap/cache";
ln -s "${thisConfig.dataDir}/storage" "$DEST/storage";
'';
};
};
pteroq = {
enable = true;
description = "Pterodactyl Queue Worker";
after = [ "redis-${cfg.redis.name}.service" ];
after = [ "redis-${thisConfig.redis.name}.service" ];
unitConfig = { StartLimitInterval = 180; };
serviceConfig = {
User = cfg.user;
Group = cfg.user;
User = thisConfig.php.user;
Group = thisConfig.php.group;
WorkingDirectory = thisConfig.php.root;
ExecStart = "${thisConfig.php.pkg}/bin/php ${thisConfig.php.root}/artisan queue:work --queue=high,standard,low --sleep=3 --tries=3";
Restart = "always";
ExecStart =
"${flakePkgs.php}/bin/php ${cfg.pkg}/artisan queue:work --queue=high,standard,low --sleep=3 --tries=3";
StartLimitBurst = 30;
RestartSec = "5s";
StartLimitBurst = 30;
};
wantedBy = [ "multi-user.target" ];
};
};
};
};
}

View file

@ -1,12 +1,12 @@
{ pkgs }:
{ pkgs ? import <nixpkgs> }:
pkgs.php83.buildEnv {
extensions = { enabled, all, }: enabled ++ (with all; [
redis
# xdebug
xdebug
]);
# extraConfig = ''
# xdebug.mode=debug
# '';
extraConfig = ''
xdebug.mode=debug
'';
}

View file

@ -1,9 +1,16 @@
{ pkgs }:
{ pkgs ? import <nixpkgs>, php ? pkgs.php }:
let
php.buildComposerProject2 (finalAttrs: {
pname = "pterodactyl-panel";
version = "1.11.11";
in pkgs.fetchzip {
url = "https://github.com/pterodactyl/panel/releases/download/v${ version }/panel.tar.gz";
src = pkgs.fetchzip {
url = "https://github.com/pterodactyl/panel/releases/download/v${finalAttrs.version}/panel.tar.gz";
hash = "sha256-0nOHtReVUVXYQY/glS4x0gkbhetoXSWg4rRwOJlkcM8=";
stripRoot = false;
}
};
vendorHash = "sha256-WBzGGWCWNqvU4Ws7wBh8Ads7RBawBaxN1QNIUI4ivOQ=";
inherit php;
})

View file

@ -1,4 +1,4 @@
{ pkgs }:
{ pkgs ? import <nixpkgs> }:
let
version = "1.11.13";