Skip to content

Update Failures

The Settings → License → Check for Updates flow downloads a signed package from license.proxrad.com, verifies the SHA-256 checksum, extracts new binaries and frontend assets to /opt/proxpanel/, then restarts the API and RADIUS containers. Four common failure modes — pick the matching log line and apply the fix.

  • “Checksum mismatch — refusing to install” toast in the UI
  • Update downloaded successfully, but containers never restart
  • Binary expired (30-day limit) and the update flow itself fails to start
  • “Failed to restart containers: Docker socket not available”
  • After a successful update, the panel returns 404 on every frontend asset
  1. Look at the update log first.

    Terminal window
    docker logs proxpanel-api 2>&1 | grep -E "Update|update" | tail -50

    The handler logs every step: download URL, file size, checksum compare, extraction, restart attempt.

  2. Confirm the package is on disk. Both the API container path and the host path matter:

    Terminal window
    docker exec proxpanel-api ls -la /app/updates/
    ls -la /opt/proxpanel/updates/
  3. Check container ages. A successful update bumps StartedAt:

    Terminal window
    docker inspect proxpanel-api --format '{{.State.StartedAt}}'
    docker inspect proxpanel-radius --format '{{.State.StartedAt}}'

    Both should reflect the time you ran the update. If only one bumped, the second container didn’t restart.

  4. Verify the running binary version:

    Terminal window
    curl -s http://localhost:8080/health | jq '.version'
    docker exec proxpanel-api /app/proisp-api -version

    These should agree after a successful update.

The handler computes SHA-256 of the downloaded .tar.gz and compares it against the value the license server returned in the update metadata. A mismatch means the file is corrupted in transit (rare) or the license server is serving a stale checksum (more common, after a re-publish).

Log line:

Update: checksum mismatch (expected=abc123..., got=def456...) - refusing to install

Diagnostic:

Terminal window
# What does the license server say the checksum should be?
curl -s -X POST "https://license.proxrad.com/api/v1/update/check" \
-H "Content-Type: application/json" \
-d '{"license_key":"PROXP-XXXXX-XXXXX-XXXXX-XXXXX","current_version":"v1.0.500"}' | jq
# What's the actual file's checksum?
sha256sum /opt/proxpanel/updates/proxpanel-v1.0.510.tar.gz

Fix:

  1. Clear the cached download and retry. The handler will fetch fresh:

    Terminal window
    rm -f /opt/proxpanel/updates/proxpanel-v*.tar.gz

    Then click Check for Updates → Install again.

  2. If retry produces the same mismatch, the license server has a bad checksum stored. Email info@proxrad.com with the version and the two checksums (expected vs got). The fix on the server side is to re-upload + re-publish the build.

  3. Sanity-check by manually downloading the package and verifying both:

    Terminal window
    curl -sLo /tmp/update.tar.gz "https://license.proxrad.com/api/v1/update/download?license_key=PROXP-XXXXX-XXXXX-XXXXX-XXXXX&version=v1.0.510"
    sha256sum /tmp/update.tar.gz

Cause 2 — Binary expired before update could run

Section titled “Cause 2 — Binary expired before update could run”

Panels are designed to be kept current and may stop running if they fall too far behind on updates. If that happens before you click Install Update, the API may not stay up long enough to run the updater.

Log line:

Container exits within seconds of start.

Fix — install the update manually:

  1. Download from the license server, off-band:

    Terminal window
    curl -sLo /opt/proxpanel/updates/proxpanel-latest.tar.gz \
    "https://license.proxrad.com/api/v1/update/download?license_key=PROXP-XXXXX-XXXXX-XXXXX-XXXXX&version=latest"
  2. Verify checksum (the license server’s /update/check endpoint returns checksum):

    Terminal window
    curl -s -X POST "https://license.proxrad.com/api/v1/update/check" \
    -H "Content-Type: application/json" \
    -d '{"license_key":"PROXP-XXXXX-XXXXX-XXXXX-XXXXX","current_version":"unknown"}' | jq '.checksum'
    sha256sum /opt/proxpanel/updates/proxpanel-latest.tar.gz
  3. Extract over the existing tree. The package contains backend/, frontend/dist/, docker-compose.yml, VERSION:

    Terminal window
    cd /opt/proxpanel
    tar -xzf updates/proxpanel-latest.tar.gz
  4. Restart containers:

    Terminal window
    docker compose restart api radius
    docker exec proxpanel-frontend nginx -s reload
  5. Confirm:

    Terminal window
    curl -s http://localhost:8080/health | jq '.version'

Cause 3 — Restart-watcher service missing

Section titled “Cause 3 — Restart-watcher service missing”

The update handler tries three ways to restart containers, in order: Docker Engine API via the mounted socket, the docker CLI inside the container, and finally a host-level systemd watcher that triggers on /opt/proxpanel/.update-complete. If you’re on an older install that never had the watcher units installed, and the Docker socket isn’t reachable, the update completes on disk but the running containers never bounce.

Log line:

Update: containers extracted but restart failed (no docker socket, no CLI, no watcher) — restart manually

Diagnostic:

Terminal window
# Is the watcher service installed?
systemctl status proxpanel-update-watcher.path proxpanel-update-watcher.service
# Is the docker socket mounted into the API container?
docker inspect proxpanel-api | grep -A2 docker.sock

Fix — install the watcher units:

  1. Create the service unit:

    Terminal window
    cat > /etc/systemd/system/proxpanel-update-watcher.service << 'EOF'
    [Unit]
    Description=ProxPanel Update Watcher
    After=docker.service
    [Service]
    Type=oneshot
    ExecStart=/bin/bash -c 'if [ -f /opt/proxpanel/.update-complete ]; then cd /opt/proxpanel && docker compose restart api radius && rm -f /opt/proxpanel/.update-complete; fi'
    [Install]
    WantedBy=multi-user.target
    EOF
  2. Create the path-watcher unit:

    Terminal window
    cat > /etc/systemd/system/proxpanel-update-watcher.path << 'EOF'
    [Unit]
    Description=Watch for ProxPanel update completion
    [Path]
    PathExists=/opt/proxpanel/.update-complete
    Unit=proxpanel-update-watcher.service
    [Install]
    WantedBy=multi-user.target
    EOF
  3. Enable:

    Terminal window
    systemctl daemon-reload
    systemctl enable --now proxpanel-update-watcher.path
  4. Retry the update. The handler will drop /opt/proxpanel/.update-complete on disk; systemd picks it up and restarts the containers from the host.

The primary restart path uses /var/run/docker.sock from inside the API container. On older docker-compose files this volume mount is absent, so the API can’t call docker restart.

Log line:

Update: Docker API restart failed: dial unix /var/run/docker.sock: no such file or directory

Diagnostic:

Terminal window
docker inspect proxpanel-api --format '{{range .Mounts}}{{println .Source "->" .Destination}}{{end}}' | grep docker.sock

Expected line: /var/run/docker.sock -> /var/run/docker.sock. If empty, the mount is missing.

Fix:

  1. Edit /opt/proxpanel/docker-compose.yml. Under the api: service’s volumes: list, add:

    - /var/run/docker.sock:/var/run/docker.sock
  2. Apply by recreating the API container (the network re-attaches cleanly):

    Terminal window
    docker compose -f /opt/proxpanel/docker-compose.yml up -d api
  3. Confirm:

    Terminal window
    docker exec proxpanel-api ls -la /var/run/docker.sock
  4. Retry the update.

Cause 5 — Frontend 404 after successful update

Section titled “Cause 5 — Frontend 404 after successful update”

Update completed, version bumped, but every page in the browser returns 404. The Docker volume mount for frontend/dist/ got broken by an overzealous rm -rf somewhere in the install path.

Symptom:

  • curl -s http://localhost/ returns nginx’s default 404
  • docker exec proxpanel-frontend ls /usr/share/nginx/html/ is empty

Fix:

  1. Re-sync the frontend without removing the directory:

    Terminal window
    cd /opt/proxpanel
    tar -xzf updates/proxpanel-latest.tar.gz frontend/
  2. Do not rm -rf frontend/dist — that breaks the Docker bind mount and nginx serves stale FS handles. Just overwrite in place.

  3. Reload nginx to pick up the new index.html cache headers:

    Terminal window
    docker exec proxpanel-frontend nginx -s reload
  4. Confirm:

    Terminal window
    curl -sI http://localhost/ | head -5

Cause 6 — nginx.conf overwritten, SSL gone

Section titled “Cause 6 — nginx.conf overwritten, SSL gone”

Older versions of the frontend-only deploy path stomped the customer’s nginx.conf with the package’s default (HTTP-only) version, wiping the SSL listener. Symptom: panel comes back on port 80 but HTTPS is dead.

Diagnostic:

Terminal window
grep -E "listen 443 ssl|ssl_certificate" /opt/proxpanel/frontend/nginx.conf

If you get no output, the SSL block is gone.

Fix:

The update handler since v1.0.95 skips overwriting nginx.conf if listen 443 ssl or ssl_certificate is present. If you’re on an older version that already wiped it, restore from a backup of /opt/proxpanel/frontend/nginx.conf and reload nginx. Then upgrade to a current version so the protection kicks in next time.

Terminal window
# Full update log
docker logs --since=24h proxpanel-api 2>&1 | grep -iE "update|version" > /tmp/update.log
# What was downloaded
ls -la /opt/proxpanel/updates/
# Compose file (check volumes / mounts)
cat /opt/proxpanel/docker-compose.yml > /tmp/compose.yml
# What the license server says is current
curl -s -X POST "https://license.proxrad.com/api/v1/update/check" \
-H "Content-Type: application/json" \
-d '{"license_key":"PROXP-XXXXX-XXXXX-XXXXX-XXXXX","current_version":"unknown"}' > /tmp/update-check.json
# Running version
curl -s http://localhost:8080/health > /tmp/health.json

Email info@proxrad.com.