Port conflicts on UGREEN
Posted by mrgrue
Heya, Tom. I'm sorry for the upcoming wall of text, but the AI suggested I copy/paste the entire conveersation.. It actually suggested I paste this conversation and the previous one, but I don't think I can still access that. Here you go: I was trying to uninstall an app, and I saw this error go by: Asking server to apply: enable=[(none)] disable=[speedtest]... sparkbox up [0;36m / | | | | ) \ \| ' \ / | '| |/ / \ / \ \/ / ) | |) | (| | | | <| |) | () < |/| ./ \,|| ||\\/ \//\\ || [0m [1mSparkBox[0m v1.6.155 - Self-Hosted Privacy Stack Created by Tom Spark | youtube.com/@TomSparkReviews Environment: NAS (generic) [0;34m[INFO][0m Starting SparkBox... [0;34m[INFO][0m Active modules: core dashboard monitoring [0;34m[INFO][0m .env changed since last up — recreating containers to pick up new values Container sb-homepage Recreate Container sb-npm Recreate Container sb-portainer Recreate Container sb-npm Recreated Container sb-portainer Recreated Container sb-homepage Recreated Container sb-homepage Starting Container sb-npm Starting Container sb-portainer Starting Container sb-homepage Started Container sb-portainer Started Error response from daemon: driver failed programming external connectivity on endpoint sb-npm (2ef7c1b576f78165c85d85bd0a1168d7216cac1c28ef436586963661aa0af86c): Error starting userland proxy: listen tcp4 0.0.0.0:443: bind: address already in use Container sb-uptime-kuma Recreate Container sb-uptime-kuma Recreated Container sb-uptime-kuma Starting Container sb-uptime-kuma Started [SparkBox] Install exited with code 1. Your pending changes are preserved — review the log above, then Apply again (or untoggle the broken module from the apply bar at the bottom). Port 443 is already bound by something else when sb-npm tries to start. That's why it exited with code 1. Find what's holding the port: sudo ss -tlnp | grep ':443' or sudo lsof -i :443 That'll tell you exactly which process has it. Common culprits on a generic/NAS platform: - A system nginx or apache installed on the host - Another reverse proxy you ran before SparkBox - Caddy if you ever tested it - Sometimes the host OS itself if it has some "web admin" panel (UGREEN, Synology, etc.) Once you know what it is: - If it's a host-level web server you don't need: stop and disable it (systemctl stop nginx && systemctl disable nginx or whatever the service is) - If it's something you want to keep: you'll need to move SparkBox's HTTPS off 443 — set HTTPSPORT=8443 (or similar) in /opt/sparkbox/.env, then re-apply After killing the conflict, hit Apply again from the dashboard and sb-npm should come up clean. Paste the output of that ss or lsof command if you're not sure what's holding it — we can figure it out from there. ↗ Didn’t fully fix it? Share to demox so Tom can ship an update grue@NAS:~$ sudo ss -tlnp | grep ':443' LISTEN 0 511 0.0.0.0:443 0.0.0.0: users:(("nginx",pid=2118995,fd=8),("nginx",pid=2118994,fd=8),("nginx",pid=2118993,fd=8),("nginx",pid=2118992,fd=8),("nginx",pid=2118991,fd=8),("nginx",pid=2118990,fd=8),("nginx",pid=2730,fd=8)) LISTEN 0 511 [::]:443 [::]: users:(("nginx",pid=2118995,fd=9),("nginx",pid=2118994,fd=9),("nginx",pid=2118993,fd=9),("nginx",pid=2118992,fd=9),("nginx",pid=2118991,fd=9),("nginx",pid=2118990,fd=9),("nginx",pid=2730,fd=9)) Yep, host nginx has 443. Check if it's a system service: systemctl status nginx If it's enabled and you don't need it (SparkBox's NPM is your reverse proxy now), just kill it: sudo systemctl stop nginx sudo systemctl disable nginx disable stops it coming back after a reboot — important, otherwise you'll hit this again next restart. Then hit Apply again from the dashboard and sb-npm should bind 443 cleanly. If you do need that nginx for something else on the host, let me know what it's doing and we'll figure out how to make them coexist. ↗ Didn’t fully fix it? Share to demox so Tom can ship an update I'm on a UGREEN NAS, and I think that nginx serves the web dashboard for that Yeah that's the classic UGREEN conflict. Their admin UI runs on 443 and you can't just kill it. You've got two options: Option 1 (recommended): Move SparkBox NPM off 443 In /opt/sparkbox/.env, change: HTTPPORT=80 HTTPSPORT=443 to something like: HTTPPORT=8080 HTTPSPORT=8443 Then re-apply from the dashboard. You'd access NPM at http://NAS-IP:8080 and https://NAS-IP:8443 going forward, and set up your proxy hosts from there. Downside: inbound traffic from the internet hits your router, so you'd need to forward ports 80/443 on your router to 8080/8443 on the NAS. Or if you're not doing remote access, no big deal. Option 2: Move the UGREEN dashboard off 443 Some UGREEN models let you change the admin UI port in their settings. Worth checking — look in the UGREEN OS web settings for "port" or "network" config. If you can move their dashboard to something like 8443, you free up 443 for SparkBox. Which way you're leaning depends on whether you need external access through a clean 443. What's your setup like — are you trying to reach SparkBox remotely, or just LAN? ↗ Didn’t fully fix it? Share to demox so Tom can ship an update We've been through this before. In the /opt/sparkbox/.env file, those env vars are set to 7080 and 7443. so i'm surprised we had a problem at all Oh interesting — if NPM is already configured for 7080/7443 in .env, it shouldn't be touching 443 at all. That's weird. Check what ports NPM is actually mapped to in the compose: docker inspect sb-npm | grep -A 20 '"Ports"' Also worth seeing the current NPM config in the stack: cat /opt/sparkbox/.env | grep -i port (just the key names are fine if you want to redact values) My hunch: either there's a stale NPM container from before SparkBox that's still bound to 443, or something in the compose override is hardcoding 443 regardless of the .env value. The docker inspect output will tell us which ports the running container actually thinks it owns. ↗ Didn’t fully fix it? Share to demox so Tom can ship an update grue@NAS:~$ docker inspect sb-npm | grep -A 20 '"Ports"' "Ports": {}, "HairpinMode": false, "LinkLocalIPv6Address": "", "LinkLocalIPv6PrefixLen": 0, "SecondaryIPAddresses": null, "SecondaryIPv6Addresses": null, "EndpointID": "", "Gateway": "", "GlobalIPv6Address": "", "GlobalIPv6PrefixLen": 0, "IPAddress": "", "IPPrefixLen": 0, "IPv6Gateway": "", "MacAddress": "", "Networks": { "sbproxy": { "IPAMConfig": null, "Links": null, "Aliases": [ "sb-npm", "npm" "Ports": {} — sb-npm is in a created state, not actually running. It never successfully started, so it's not holding any ports. That tracks with the error we saw. But also means something else tried to bind 443 during the sparkbox up and failed. The error log showed sb-npm was the one that errored... but if your .env has 7080/7443, NPM shouldn't be touching 443 at all. Can you run: grep -i port /opt/sparkbox/.env Just paste the key names and values for the port-related ones — specifically HTTPPORT and HTTPSPORT. I want to confirm what's actually in the file vs. what we're assuming is there. Sometimes a previous apply writes new values and it's not what you expect. ↗ Didn’t fully fix it? Share to demox so Tom can ship an update --- PORTS --- HTTPPORT=7080 HTTPSPORT=7443 PIHOLEDNSPORT=53 PORTAINERPORT=9000 OK so the .env is correct — 7080/7443, not 443. That's not the problem. Something's off with how the compose is picking these up, or there's a hardcoded 443 somewhere in the NPM compose config. Let me see the actual compose definition SparkBox is using for sb-npm: cat /opt/sparkbox/modules/core/docker-compose.yml | grep -A 20 'npm' or it might be in a separate file: find /opt/sparkbox -name ".yml" | xargs grep -l "sb-npm" 2/dev/null Paste whichever one has the npm service definition. I want to see if 443 is hardcoded in there rather than using the ${HTTPSPORT} variable — that would explain exactly what we're seeing. ↗ Didn’t fully fix it? Share to demox so Tom can ship an update grue@NAS:~$ cat /opt/sparkbox/modules/core/docker-compose.yml | grep -A 20 'npm' - sb-npm - sb-dashboard services: npm: friendlyname: "Nginx Proxy Manager" description: "Routes your domain to the right service and manages SSL certificates" portmap: 81 loopbackonly: true port 81 binds to 127.0.0.1 only; the dashboard's launcher renders this as "local-only access" with SSH-tunnel instructions instead of a LAN link that would always refuse connection. The dashboard's own Settings → Domains UI uses NPM's admin API directly via the sbproxy docker network, so most users never need the raw admin UI. tip: "Most domain/SSL tasks can be done from Settings → Domains. If you need the raw NPM admin UI, use an SSH tunnel (see Settings → Help)." portainer: firstlogin: "Log in as 'admin' with the password SparkBox generated for you at install time. You can find it in state/portainer-admin-password.txt on the host, or run: sudo cat /volume1/docker/sparkbox/state/portainer-admin-password.txt" friendlyname: "Portainer" description: "Visual tool for managing Docker containers, checking logs, and debugging" portmap: 9000 tip: "Useful for troubleshooting. If a service won't start, check its logs here." homepage: friendlyname: "Homepage" description: "A personal start page showing all your services with live status" -- npm: image: jc21/nginx-proxy-manager:2.14.0 containername: sb-npm ports: - "${HTTPPORT:-80}:80" - "${HTTPSPORT:-443}:443" v1.6.36: was 127.0.0.1:81 (loopback only) for defense-in-depth. Same problem the Authelia binding had in v1.6.35 — the dashboard tile pointed at the LAN IP so every fresh install showed ERRCONNECTIONREFUSED for "Nginx Proxy Manager" until the user SSH-tunneled. NPM has its own login form on /admin so a LAN attacker hitting port 81 still hits auth, not a bypass — the loopback restriction was redundant with NPM's own gate. Caught by headless verification on 2026-05-04. Default NPM admin creds are admin@example.com / changeme, surfaced as a first-login prompt by the dashboard. - "81:81" environment: - TZ=${TZ:-UTC} volumes: - ${SBROOT:-/opt/sparkbox}/modules/core/config/npm/data:/data - ${SBROOT:-/opt/sparkbox}/modules/core/config/npm/letsencrypt:/etc/letsencrypt networks: - sbproxy deploy: resources: limits: memory: 256M cpus: '0.50' securityopt: - no-new-privileges:true healthcheck: test: ["CMD", "curl", "-f", "http://localhost:81/api/"] interval: 30s timeout: 10s retries: 3 startperiod: 30s logging: driver: json-file options: max-size: "10m" max-file: "3" Found it. The compose has: - "${HTTPSPORT:-443}:443" That :-443 is the default fallback, but the container-side port is hardcoded to 443. So regardless of what HTTPSPORT is set to on the host, NPM always listens on 443 inside the container — and the host binding maps 7443→443. That part's fine. The actual problem: the host side ${HTTPSPORT:-443} resolved correctly to 7443... so why did it try to bind 443? Wait — look at this line: - "81:81" That's fine. But go back to the error message from earlier: listen tcp4 0.0.0.0:443: bind: address already in use That's port 443 on the host. With HTTPSPORT=7443 that shouldn't happen. I'm not 100% sure what's causing the mismatch between what's in .env and what Docker actually tried to bind. Could be a compose caching issue where an old version of the file was used during that apply. Try this — force a clean re-read: cd /opt/sparkbox && sudo docker compose pull sb-npm 2/dev/null; sudo sparkbox up If it still tries to bind 443, paste the full sparkbox up output and I'd say hit the "Share to demox" button — this looks like it might need Tom to look at the compose variable substitution directly. ↗ Didn’t fully fix it? Share to demox so Tom can ship an update grue@NAS:~$ cd /opt/sparkbox && sudo docker compose pull sb-npm 2/dev/null; sudo sparkbox up / | | | | ) \ \| ' \ / | '| |/ / \ / \ \/ / ) | |) | (| | | | <| |) | () < |/| ./ \,|| ||\\/ \//\\ || SparkBox v1.6.155 - Self-Hosted Privacy Stack Created by Tom Spark | youtube.com/@TomSparkReviews Environment: NAS (generic) [INFO] Starting SparkBox... [INFO] Active modules: core dashboard monitoring [INFO] Prepared 3 module config dir(s) for PUID=1000 PGID=10 [INFO] .env changed since last up — recreating containers to pick up new values [+] Running 3/3 ✔ Container sb-portainer Started 0.3s ✔ Container sb-homepage Started 0.5s ✔ Container sb-npm Started 0.1s [+] Running 1/1 ✔ Container sb-uptime-kuma Started 4.5s [OK] SparkBox is running! Service URLs: Core Infrastructure: Nginx Proxy Manager: http://tinkers-nas.local:81 Portainer: http://tinkers-nas.local:9000 Homepage: http://tinkers-nas.local:3000 SparkBox Dashboard: SparkBox Dashboard: http://tinkers-nas.local:8443 Monitoring: Uptime Kuma: http://tinkers-nas.local:3001 There you go — all 4 containers started clean, no port conflict. sb-npm is up and happy. My best guess on what happened earlier: the .env changed since last up detection triggered a recreate mid-apply during the speedtest uninstall, and Docker briefly tried to use stale config before the new values were flushed. The docker compose pull + fresh sparkbox up forced it to re-read everything from scratch. You should be good now. The speedtest module is gone, all your core/dashboard/monitoring containers are running. ↗ Didn’t fully fix it? Share to demox so Tom can ship an update I just tried to re-install the speedtest tracker, and I got this: Asking server to apply: enable=[speedtest] disable=[(none)]... sparkbox up [0;36m / | | | | ) \ \| ' \ / | '| |/ / \ / \ \/ / ) | |) | (| | | | <| |) | () < |/| ./ \,|| ||\\/ \//\\ || [0m [1mSparkBox[0m v1.6.155 - Self-Hosted Privacy Stack Created by Tom Spark | youtube.com/@TomSparkReviews Environment: NAS (generic) [0;34m[INFO][0m Starting SparkBox... [0;34m[INFO][0m Active modules: core dashboard monitoring speedtest Container sb-portainer Running Container sb-homepage Running Container sb-npm Recreate Container sb-npm Recreated Container sb-npm Starting Error response from daemon: driver failed programming external connectivity on endpoint sb-npm (2bde42555bc5c1b856dfe54232bf4ef6959052120d34b036eb318c506058a364): Error starting userland proxy: listen tcp4 0.0.0.0:443: bind: address already in use Container sb-uptime-kuma Running time="2026-06-04T06:44:02Z" level=warning msg="The \"SPEEDTESTAPPKEY\" variable is not set. Defaulting to a blank string." Container sb-speedtest Starting Error response from daemon: driver failed programming external connectivity on endpoint sb-speedtest (921a683c7a32b9f90771da10d4f79c260752241b40f74978434ecf2f4aa5ed50): Error starting userland proxy: listen tcp4 0.0.0.0:8765: bind: address already in use [SparkBox] Install exited with code 1. Your pending changes are preserved — review the log above, then Apply again (or untoggle the broken module from the apply bar at the bottom). Two separate port conflicts this time — 443 again, plus 8765 for speedtest. And it's happening every time a module change triggers an sb-npm recreate. The 443 thing is the same pattern as before — something about the recreate cycle is causing Docker to momentarily try binding 443 even though your HTTPSPORT=7443. That's suspicious enough that I think there's a real bug here in how SparkBox handles container recreates on UGREEN where the host nginx already owns 443. The 8765 conflict is separate — something else on the host has that port: sudo ss -tlnp | grep ':8765' But honestly, the 443 issue recurring on every npm recreate is the bigger problem and I'm not confident I have the full picture on why it keeps happening. I'd hit the "Share to demox" button on this one — paste both the original conversation and this new output. Tom should see that sb-npm is repeatedly trying to bind 443 during recreates despite HTTPSPORT=7443, specifically on UGREEN where host nginx owns 443. That pattern needs a proper look.
3 replies
tomspark wrote:
Thanks for the detailed paste — the AI's right that this needs a real look, and you've pinned the key thing: sb-npm keeps grabbing port 443, which collides with UGOS's own nginx. That happens when your HTTPSPORT setting isn't actually reaching npm when it recreates, so it falls back to the default (443). Let's confirm what it's set to: sudo grep -n HTTPSPORT /opt/sparkbox/.env Paste that — I want to see if there's exactly ONE line saying HTTPSPORT=7443, or a stray empty one (HTTPSPORT=) overriding it, which would explain the fallback to 443. Once I see it I'll give you the exact fix to make npm bind 7443 instead. Good instinct hitting Share-to-demox on this — it's exactly the kind of UGREEN-specific thing worth flagging.
mrgrue wrote:
Here's the output: 20:HTTPSPORT=7443 In case it matters, I'd run the latest SB core update as well as the app updates before I saw this message; sorry about that.
tomspark wrote:
Perfect, that's exactly what I needed — and it clears you of any blame. Your setting is spot on: one clean line, 7443, no stray duplicate. So this one's on our side. What's happening is that when an update or app-change runs from the dashboard, your 7443 isn't reaching Nginx Proxy Manager, so it falls back to the default 443 and bumps into UGOS's own web server. Quick way to get you going right now — run this straight in the SSH terminal you just used: sudo sparkbox up Run from there it picks up your 7443 properly, and NPM should stop fighting for 443. Give it a minute, then check it's behaving. Meanwhile I've found the root cause and I'm shipping a proper fix so it sticks through dashboard updates too. Thanks for the clear paste!