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
24 changes: 24 additions & 0 deletions backend/internal/nginx.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -285,6 +287,7 @@ const internalNginx = {
}

certificate.ipv6 = internalNginx.ipv6Enabled();
certificate.http_port = internalNginx.getHttpPort();

renderEngine
.parseAndRender(template, certificate)
Expand Down Expand Up @@ -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;
12 changes: 6 additions & 6 deletions backend/templates/_listen.conf
Original file line number Diff line number Diff line change
@@ -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: " " }};
Expand Down
6 changes: 3 additions & 3 deletions backend/templates/default.conf
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
4 changes: 2 additions & 2 deletions backend/templates/letsencrypt-request.conf
Original file line number Diff line number Diff line change
@@ -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: " " }};
Expand Down
3 changes: 2 additions & 1 deletion docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/00-all.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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
40 changes: 40 additions & 0 deletions docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/60-ports.sh
Original file line number Diff line number Diff line change
@@ -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"
Copy link
Member

@jc21 jc21 Feb 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see what you're trying to do here and I'm glad you thought of this. Something I would consider though..

This script runs on each startup, yet it seems like a run-once migration. Some options that might be better:

  1. create a migration, they're not just for databases. In this case it could iterate over all hosts and regenerate their nginx config, however there are risks doing this too
  2. Or keep this file but don't be specific with 80/443. Consider what should happen if I change to 8080/8443 and then change to 8888/9999. I'd expect this script to handle it

Option 2 is probably my preference here.

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
17 changes: 17 additions & 0 deletions docs/src/advanced-config/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -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).