Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions webserver/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Nginx + PHP Webserver
> Simple Nginx and PHP webserver egg for hosting PHP sites and apps.

Use this egg to run a lightweight webserver with PHP support.

Optional install/startup features:
- WORDPRESS=1 downloads and unpacks WordPress into /home/container/www during installation.
- COMPOSER_STATUS=1 runs Composer on startup (optionally using COMPOSER_MODULES if no composer.json is present).

Base scripts credit: https://github.com/Ym0T/pterodactyl-nginx-egg
66 changes: 66 additions & 0 deletions webserver/egg-webserver-egg.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
{
"_comment": "DO NOT EDIT: FILE GENERATED AUTOMATICALLY BY PTERODACTYL PANEL - PTERODACTYL.IO",
"meta": {
"version": "PTDL_v2",
"update_url": null
},
"exported_at": "2026-08-01T18:14:02+00:00",
"name": "PHP Webserver Egg",
"author": "admin@gglvxd.net",
"description": "Pterodactyl Nginx PHP Egg",
"features": [],
"docker_images": {
"ghcr.io\/ym0t\/pterodactyl-nginx-egg:8.5-latest": "ghcr.io\/ym0t\/pterodactyl-nginx-egg:8.5-latest",
"ghcr.io\/ym0t\/pterodactyl-nginx-egg:8.4-latest": "ghcr.io\/ym0t\/pterodactyl-nginx-egg:8.4-latest",
"ghcr.io\/ym0t\/pterodactyl-nginx-egg:8.3-latest": "ghcr.io\/ym0t\/pterodactyl-nginx-egg:8.3-latest",
"ghcr.io\/ym0t\/pterodactyl-nginx-egg:8.2-latest": "ghcr.io\/ym0t\/pterodactyl-nginx-egg:8.2-latest",
"ghcr.io\/ym0t\/pterodactyl-nginx-egg:8.1-latest": "ghcr.io\/ym0t\/pterodactyl-nginx-egg:8.1-latest"
},
"file_denylist": [],
"startup": ".\/start.sh",
"config": {
"files": "{\r\n \"core\/nginx\/conf.d\/default.conf\": {\r\n \"parser\": \"file\",\r\n \"find\": {\r\n \" listen\": \" listen {{server.build.default.port}};\"\r\n }\r\n }\r\n}",
"startup": "{\r\n \"done\": \"webserver started\"\r\n}",
"logs": "{\r\n \"location\": \"logs\/latest.log\"\r\n}",
"stop": "^C & ^C"
},
"scripts": {
"installation": {
"script": "#!\/bin\/bash\r\n\r\nset -u\r\n\r\n# [SETUP] Install necessary packages, including git\r\necho -e \"[SETUP] Install packages\"\r\napt-get update -qq > \/dev\/null 2>&1\r\napt-get install -qq > \/dev\/null 2>&1 -y git wget perl perl-doc fcgiwrap pkg-php-tools\r\n\r\n\r\n#redis\r\napt-get install -qq -y php-redis\r\n\r\n# Change to server directory\r\ncd \/mnt\/server\r\n\r\n# [SETUP] Create necessary folders\r\necho -e \"[SETUP] Create folders\"\r\nmkdir -p logs\r\n\r\n# Clone only the required directory from the repository\r\necho \"[Git] Cloning webserver\/files from application-eggs repository.\"\r\ngit clone --filter=blob:none --sparse https:\/\/github.com\/pterodactyl\/application-eggs.git \/mnt\/server\/gtemp > \/dev\/null 2>&1 && echo \"[Git] Repository cloned successfully.\" || { echo \"[Git] Error: Repository clone failed.\"; exit 21; }\r\n\r\ncd \/mnt\/server\/gtemp\r\n\r\necho \"[Git] Checking out webserver\/files directory.\"\r\ngit sparse-checkout set webserver\/files > \/dev\/null 2>&1 || { echo \"[Git] Error: Sparse checkout failed.\"; exit 22; }\r\n\r\n# Copy the contents of webserver\/files into \/home\/container\r\necho \"[Git] Copying webserver\/files contents to \/home\/container.\"\r\ncp -r \/mnt\/server\/gtemp\/webserver\/files\/* \/mnt\/server || { echo \"[Git] Error: Copying files failed.\"; exit 23; }\r\n\r\necho \"[Git] Files copied successfully.\"\r\n\r\nchmod +x \/mnt\/server\/start.sh\r\nchmod +x \/mnt\/server\/core\/nginx.sh\r\nchmod +x \/mnt\/server\/core\/composer.sh\r\n\r\n# Remove the temporary cloned repository\r\nrm -rf \/mnt\/server\/gtemp\r\n\r\n#create temporary directory\r\nmkdir -p \/mnt\/server\/core\/tmp\r\n\r\n# Check if WordPress should be installed\r\nif [ \"${WORDPRESS}\" == \"true\" ] || [ \"${WORDPRESS}\" == \"1\" ]; then\r\n echo \"[SETUP] Install WordPress\"\r\n cd \/mnt\/server\/www\r\n wget -q http:\/\/wordpress.org\/latest.tar.gz > \/dev\/null 2>&1 || { echo \"[SETUP] Error: Downloading WordPress failed.\"; exit 16; }\r\n tar xzf latest.tar.gz > \/dev\/null 2>&1\r\n mv wordpress\/* .\r\n rm -rf wordpress latest.tar.gz\r\n echo \"[SETUP] WordPress installed - http:\/\/ip:port\/wp-admin\"\r\nfi\r\n\r\n\r\necho -e \"[DONE] Everything has been installed successfully\"\r\necho -e \"[INFO] You can now start the nginx web server\"",
"container": "debian:bookworm-slim",
"entrypoint": "bash"
}
},
"variables": [
{
"name": "Wordpress",
"description": "Enable or disable Wordpress \r\n\r\n0 = false (default) \r\n1 = true",
"env_variable": "WORDPRESS",
"default_value": "0",
"user_viewable": true,
"user_editable": true,
"rules": "required|boolean",
"field_type": "text"
},
{
"name": "Enable Composer Module",
"description": "Enable or disable Composer package installation",
"env_variable": "COMPOSER_STATUS",
"default_value": "0",
"user_viewable": true,
"user_editable": true,
"rules": "required|boolean",
"field_type": "text"
},
{
"name": "Composer Packages",
"description": "Space-separated list of Composer packages to install",
"env_variable": "COMPOSER_MODULES",
"default_value": "",
"user_viewable": true,
"user_editable": true,
"rules": "nullable|string",
"field_type": "text"
}
]
}
44 changes: 44 additions & 0 deletions webserver/files/core/composer.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
#!/usr/bin/env bash
set -euo pipefail
trap 'echo "[composer] Error on line $LINENO"' ERR

# Configuration via environment variables
COMPOSER_STATUS="${COMPOSER_STATUS:-false}"
COMPOSER_MODULES="${COMPOSER_MODULES:-}"
CACHE_DIR="${COMPOSER_CACHE_DIR:-/home/container/core/.cache/composer}"
WWW_DIR="${COMPOSER_WWW_DIR:-/home/container/www}"
COMPOSER_JSON="$WWW_DIR/composer.json"

# Function to check if Composer should be enabled
enabled() { [[ "$1" =~ ^(true|1)$ ]]; }

# Skip if Composer is disabled
if ! enabled "$COMPOSER_STATUS"; then
exit 0
fi

# Ensure the Composer cache directory exists
mkdir -p "$CACHE_DIR"
export COMPOSER_CACHE_DIR="$CACHE_DIR"

echo "[Composer] Installing Composer packages"

# Prefer composer.json if it exists
if [[ -f "$COMPOSER_JSON" ]]; then
echo "[Composer] composer.json found. Running install..."
composer install \
--working-dir="$WWW_DIR" \
--no-interaction
echo "[Composer] Composer install complete"

# Fallback to COMPOSER_MODULES if composer.json is missing
elif [[ -n "$COMPOSER_MODULES" ]]; then
echo "[Composer] composer.json not found. Installing from COMPOSER_MODULES: $COMPOSER_MODULES"
read -r -a modules <<< "$COMPOSER_MODULES"
composer require "${modules[@]}" --working-dir="$WWW_DIR" --no-interaction
echo "[Composer] Composer module installation complete"

# No installation source found
else
echo "[Composer] No composer.json and no modules specified. Skipping."
fi
43 changes: 43 additions & 0 deletions webserver/files/core/nginx.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
#!/usr/bin/env bash
set -euo pipefail
trap 'echo "Error on line $LINENO"' ERR

# Configurable paths/files via env vars with defaults
PHP_INI="${PHP_INI:-/home/container/core/php/php.ini}"
PHP_FPM_CONF="${PHP_FPM_CONF:-/home/container/core/php/php-fpm.conf}"
NGINX_CONF="${NGINX_CONF:-/home/container/core/nginx/nginx.conf}"
NGINX_PREFIX="${NGINX_PREFIX:-/home/container}"

# Locate the available php-fpm binary
PHP_FPM_BIN=""
for candidate in php-fpm php-fpm8.5 php-fpm8.4 php-fpm8.3 php-fpm8.2 php-fpm8.1 php8.5-fpm php8.4-fpm php8.3-fpm php8.2-fpm php8.1-fpm; do
if command -v "$candidate" >/dev/null 2>&1; then
PHP_FPM_BIN="$candidate"
break
fi
done

if [[ -z "$PHP_FPM_BIN" ]]; then
echo "ERROR: Could not find a usable php-fpm binary."
exit 1
fi

# Start PHP-FPM
echo "Starting PHP-FPM"
echo "Launching ${PHP_FPM_BIN}"
"$PHP_FPM_BIN" \
-c "$PHP_INI" \
--fpm-config "$PHP_FPM_CONF" \
--daemonize > /dev/null 2>&1 || {
echo "ERROR: Failed to launch ${PHP_FPM_BIN}."
exit 1
}

# Success message
echo "webserver started"

# Brief pause
sleep 1

# Start NGINX
nginx -c "$NGINX_CONF" -p "$NGINX_PREFIX" -e /dev/stderr
66 changes: 66 additions & 0 deletions webserver/files/core/nginx/conf.d/default.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
server {
listen 80;
server_name "";

root /home/container/www;
index index.html index.htm index.php;
charset utf-8;

absolute_redirect off;
port_in_redirect off;

location / {
try_files $uri $uri/ /index.php?$query_string;
}

location = /favicon.ico { access_log off; log_not_found off; }
location = /robots.txt { access_log off; log_not_found off; }

# allow larger file uploads and longer script runtimes
client_max_body_size 100m;
client_body_timeout 120s;
sendfile off;

location ~ \.php$ {
fastcgi_split_path_info ^(.+\.php)(/.+)$;
try_files $fastcgi_script_name =404;
fastcgi_pass unix:/home/container/core/tmp/php-fpm.sock;
fastcgi_index index.php;
include fastcgi_params;
fastcgi_param PHP_VALUE "upload_max_filesize = 100M \n post_max_size=100M";
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}

# Block access to sensitive files and directories
location ~ /\.git {
deny all;
}

location ~ /\.ht {
deny all;
}

location ~ /\.env {
deny all;
}

location ~ /\.svn {
deny all;
}

location ~ /composer\.(json|lock)$ {
deny all;
}

location ~ /package(-lock)?\.json$ {
deny all;
}

location ~ /\.user\.ini$ {
deny all;
}

location ~ /\.(bak|config|sql|fla|psd|ini|log|sh|inc|swp|dist)$ {
deny all;
}
}
27 changes: 27 additions & 0 deletions webserver/files/core/nginx/fastcgi.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@

fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param QUERY_STRING $query_string;
fastcgi_param REQUEST_METHOD $request_method;
fastcgi_param CONTENT_TYPE $content_type;
fastcgi_param CONTENT_LENGTH $content_length;

fastcgi_param SCRIPT_NAME $fastcgi_script_name;
fastcgi_param REQUEST_URI $request_uri;
fastcgi_param DOCUMENT_URI $document_uri;
fastcgi_param DOCUMENT_ROOT $document_root;
fastcgi_param SERVER_PROTOCOL $server_protocol;
fastcgi_param REQUEST_SCHEME $scheme;
fastcgi_param HTTPS $https if_not_empty;

fastcgi_param GATEWAY_INTERFACE CGI/1.1;
fastcgi_param SERVER_SOFTWARE nginx/$nginx_version;

fastcgi_param REMOTE_ADDR $remote_addr;
fastcgi_param REMOTE_PORT $remote_port;
fastcgi_param REMOTE_USER $remote_user;
fastcgi_param SERVER_ADDR $server_addr;
fastcgi_param SERVER_PORT $server_port;
fastcgi_param SERVER_NAME $server_name;

# PHP only, required if PHP was built with --enable-force-cgi-redirect
fastcgi_param REDIRECT_STATUS 200;
26 changes: 26 additions & 0 deletions webserver/files/core/nginx/fastcgi_params
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@

fastcgi_param QUERY_STRING $query_string;
fastcgi_param REQUEST_METHOD $request_method;
fastcgi_param CONTENT_TYPE $content_type;
fastcgi_param CONTENT_LENGTH $content_length;

fastcgi_param SCRIPT_NAME $fastcgi_script_name;
fastcgi_param REQUEST_URI $request_uri;
fastcgi_param DOCUMENT_URI $document_uri;
fastcgi_param DOCUMENT_ROOT $document_root;
fastcgi_param SERVER_PROTOCOL $server_protocol;
fastcgi_param REQUEST_SCHEME $scheme;
fastcgi_param HTTPS $https if_not_empty;

fastcgi_param GATEWAY_INTERFACE CGI/1.1;
fastcgi_param SERVER_SOFTWARE nginx/$nginx_version;

fastcgi_param REMOTE_ADDR $remote_addr;
fastcgi_param REMOTE_PORT $remote_port;
fastcgi_param REMOTE_USER $remote_user;
fastcgi_param SERVER_ADDR $server_addr;
fastcgi_param SERVER_PORT $server_port;
fastcgi_param SERVER_NAME $server_name;

# PHP only, required if PHP was built with --enable-force-cgi-redirect
fastcgi_param REDIRECT_STATUS 200;
Loading