diff --git a/backend/internal/nginx.js b/backend/internal/nginx.js index fe84607f96..97fa71aa0e 100644 --- a/backend/internal/nginx.js +++ b/backend/internal/nginx.js @@ -240,6 +240,8 @@ const internalNginx = { // Set the IPv6 setting for the host host.ipv6 = internalNginx.ipv6Enabled(); + host.http_port = internalNginx.getHttpPort(); + host.https_port = internalNginx.getHttpsPort(); locationsPromise.then(() => { renderEngine @@ -285,6 +287,7 @@ const internalNginx = { } certificate.ipv6 = internalNginx.ipv6Enabled(); + certificate.http_port = internalNginx.getHttpPort(); renderEngine .parseAndRender(template, certificate) @@ -432,6 +435,27 @@ const internalNginx = { return true; }, + + /** + * @returns {number} + */ + getHttpPort: () => { + return Number.parseInt(process.env.HTTP_PORT, 10) || 80; + }, + + /** + * @returns {number} + */ + getHttpsPort: () => { + return Number.parseInt(process.env.HTTPS_PORT, 10) || 443; + }, + + /** + * @returns {number} + */ + getWebUiPort: () => { + return Number.parseInt(process.env.WEB_UI_PORT, 10) || 81; + }, }; export default internalNginx; diff --git a/backend/templates/_listen.conf b/backend/templates/_listen.conf index 34a808e6a0..6448023b7f 100644 --- a/backend/templates/_listen.conf +++ b/backend/templates/_listen.conf @@ -1,15 +1,15 @@ - listen 80; + listen {{ http_port }}; {% if ipv6 -%} - listen [::]:80; + listen [::]:{{ http_port }}; {% else -%} - #listen [::]:80; + #listen [::]:{{ http_port }}; {% endif %} {% if certificate -%} - listen 443 ssl; + listen {{ https_port }} ssl; {% if ipv6 -%} - listen [::]:443 ssl; + listen [::]:{{ https_port }} ssl; {% else -%} - #listen [::]:443; + #listen [::]:{{ https_port }}; {% endif %} {% endif %} server_name {{ domain_names | join: " " }}; diff --git a/backend/templates/default.conf b/backend/templates/default.conf index cc590f9d85..c68171953c 100644 --- a/backend/templates/default.conf +++ b/backend/templates/default.conf @@ -5,11 +5,11 @@ # Skipping output, congratulations page configration is baked in. {%- else %} server { - listen 80 default; + listen {{ http_port }} default; {% if ipv6 -%} - listen [::]:80 default; + listen [::]:{{ http_port }} default; {% else -%} - #listen [::]:80 default; + #listen [::]:{{ http_port }} default; {% endif %} server_name default-host.localhost; access_log /data/logs/default-host_access.log combined; diff --git a/backend/templates/letsencrypt-request.conf b/backend/templates/letsencrypt-request.conf index 676c8a60fd..211cd9ed03 100644 --- a/backend/templates/letsencrypt-request.conf +++ b/backend/templates/letsencrypt-request.conf @@ -1,9 +1,9 @@ {% include "_header_comment.conf" %} server { - listen 80; + listen {{ http_port }}; {% if ipv6 -%} - listen [::]:80; + listen [::]:{{ http_port }}; {% endif %} server_name {{ domain_names | join: " " }}; diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/00-all.sh b/docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/00-all.sh index d2e62f3bbc..13b0ab4495 100755 --- a/docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/00-all.sh +++ b/docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/00-all.sh @@ -18,5 +18,6 @@ fi . /etc/s6-overlay/s6-rc.d/prepare/30-ownership.sh . /etc/s6-overlay/s6-rc.d/prepare/40-dynamic.sh . /etc/s6-overlay/s6-rc.d/prepare/50-ipv6.sh -. /etc/s6-overlay/s6-rc.d/prepare/60-secrets.sh +. /etc/s6-overlay/s6-rc.d/prepare/60-ports.sh +. /etc/s6-overlay/s6-rc.d/prepare/70-secrets.sh . /etc/s6-overlay/s6-rc.d/prepare/90-banner.sh diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/60-ports.sh b/docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/60-ports.sh new file mode 100755 index 0000000000..8c98a4c06c --- /dev/null +++ b/docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/60-ports.sh @@ -0,0 +1,40 @@ +#!/command/with-contenv bash +# shellcheck shell=bash + +set -e + +log_info 'Ports ...' + +# Default ports +HTTP_PORT=${HTTP_PORT:-80} +HTTPS_PORT=${HTTPS_PORT:-443} +WEB_UI_PORT=${WEB_UI_PORT:-81} + +process_folder () { + echo "Processing folder: $1" + FILES=$(find "$1" -type f -name "*.conf") + + for FILE in $FILES + do + echo "- ${FILE}" + # Replace HTTP port + sed -i -E "s/listen 80([^0-9]|$)/listen ${HTTP_PORT}\1/g" "$FILE" + sed -i -E "s/listen \[::\]:80([^0-9]|$)/listen [::]:${HTTP_PORT}\1/g" "$FILE" + sed -i -E "s/set \$port \"80\"/set \$port \"${HTTP_PORT}\"/g" "$FILE" + + # Replace HTTPS port + sed -i -E "s/listen 443([^0-9]|$)/listen ${HTTPS_PORT}\1/g" "$FILE" + sed -i -E "s/listen \[::\]:443([^0-9]|$)/listen [::]:${HTTPS_PORT}\1/g" "$FILE" + sed -i -E "s/set \$port \"443\"/set \$port \"${HTTPS_PORT}\"/g" "$FILE" + + # Replace Web UI port + sed -i -E "s/listen 81([^0-9]|$)/listen ${WEB_UI_PORT}\1/g" "$FILE" + sed -i -E "s/listen \[::\]:81([^0-9]|$)/listen [::]:${WEB_UI_PORT}\1/g" "$FILE" + done + + # ensure the files are still owned by the npm user + chown -R "$PUID:$PGID" "$1" +} + +process_folder /etc/nginx/conf.d +process_folder /data/nginx diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/60-secrets.sh b/docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/70-secrets.sh similarity index 100% rename from docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/60-secrets.sh rename to docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/70-secrets.sh diff --git a/docs/src/advanced-config/index.md b/docs/src/advanced-config/index.md index e4a9594e0e..e72a6290e7 100644 --- a/docs/src/advanced-config/index.md +++ b/docs/src/advanced-config/index.md @@ -237,4 +237,21 @@ Setting these environment variables will create the default user on startup, ski environment: INITIAL_ADMIN_EMAIL: my@example.com INITIAL_ADMIN_PASSWORD: mypassword1 + +## Custom Ports + +If you need to change the default ports that NPM listens on (80 for HTTP, 443 for HTTPS, and 81 for the Web UI), you can do so by setting environment variables. Note that you must also update your Docker port mappings to match these internal ports. + +```yml + ports: + - '8080:8080' # Public HTTP Port + - '8443:8443' # Public HTTPS Port + - '8181:8181' # Admin Web Port + environment: + HTTP_PORT: 8080 + HTTPS_PORT: 8443 + WEB_UI_PORT: 8181 ``` + +> [!IMPORTANT] +> When changing these environment variables, the internal Nginx configuration will be updated to listen on these new ports. Your Docker `ports` mapping must use these new ports as the container-side port (the second number).