Troubleshooting
Fix common problems with a self-hosted Vulnsy installation, from startup errors and license status to file downloads, email delivery, sign-in and AI endpoints.
This page covers problems specific to running Vulnsy yourself. For problems inside the app, such as report exports, see the general Troubleshooting section.
Collect Information
sudo /opt/vulnsy/install.sh status # containers, health, version and license status
sudo /opt/vulnsy/install.sh logs app # follow the app log (Ctrl+C to stop)
curl -sS https://reports.example.com/api/health
curl -sS https://reports.example.com/api/versionEvery run of the installer is logged in /opt/vulnsy/install.log. To run docker compose commands yourself, such as docker compose ps, run them as root from /opt/vulnsy, the directory that holds docker-compose.yml and .env.
Useful text to look for in the app log:
| Log text | Meaning |
|---|---|
[self-hosted] | Startup steps: instance ID, organization and first administrator |
[health] | A failed health check, with the error |
S3 storage: | The storage settings in use |
Email: | The email settings in use |
Installer
| Symptom | Cause | Fix |
|---|---|---|
./install.sh fails with Permission denied | The package was extracted on a file system mounted without execute permission, often /tmp | Extract the package somewhere else, such as your home directory, or run sudo bash install.sh ... |
the package does not match its SHA256SUMS | A file of the package is damaged or was changed, often by an incomplete download | Download the package again, and check it with sha256sum -c against its .sha256 file before extracting it |
is not supported for the operating system | The server does not run a supported distribution | Use a supported server, or add --force to try anyway |
ports in use | Another service listens on port 80, 443, 3000 or 9000 | Stop that service, or choose other ports with --app-port and --minio-port. For 80 and 443, use --no-tls with your own reverse proxy. |
is built for linux/amd64 but this server is linux/arm64 | The package is for 64-bit x86 servers | Install on an x86-64 (amd64) server |
Vulnsy data volumes exist but /opt/vulnsy/.env does not | An earlier installation left its data behind, without its .env | Put that installation's .env back in the install directory, or restore a backup with install.sh restore. To delete the old data instead, run the docker volume rm command the message shows. |
Vulnsy is already installed in another directory | Only one installation can run on a server | Pass that directory with --home, or uninstall it first |
Startup
| Symptom | Cause | Fix |
|---|---|---|
docker compose stops with run docker/generate-env.sh to create .env or required variable VULNSY_VERSION is missing a value | The command was not run from the install directory, or .env is missing or damaged | Run docker compose from /opt/vulnsy, which holds docker-compose.yml and .env. If VULNSY_VERSION is missing from .env, run sudo ./install.sh upgrade from the package of the installed version. |
Starting the app fails with No such image: vulnsy/vulnsy | VULNSY_VERSION in .env names a version whose package was never loaded | Set VULNSY_VERSION back, or run sudo ./install.sh upgrade from the package of the version you want |
The browser shows Vulnsy is not ready. An administrator should check the server logs for [self-hosted] errors. (HTTP 503) | The first start has not finished, or a startup step failed: for example, INITIAL_ADMIN_EMAIL or INITIAL_ADMIN_PASSWORD is missing, or a database cannot be reached | Read the app log. If startup is still running, wait for it. If a [self-hosted] line reports an error, fix the setting it names in .env and run docker compose up -d app. |
docker compose ps shows the app as health: starting or unhealthy during the first start | The first start applies the schema and loads the Vulnsy library before it serves requests. The healthcheck allows 60 seconds before failures count, and the first start can take longer. | Wait, and follow the app log. The status changes to healthy once startup has finished and /api/health returns 200. |
/api/health returns HTTP 503 with "status":"degraded" | A database check failed. The checks field names it (controlPlane, shared or tenant), and a log line starting with [health] gives the error. | Check that the postgres container is running and healthy, and that POSTGRES_PASSWORD and the database URLs in .env match the database |
| The browser shows 502 Bad Gateway, with the bundled Caddy | The app container is not running, or has not finished starting | Check docker compose ps and the app log |
| The app keeps restarting, and its log says the database was last run by a newer version | An older version was started against a database that a newer version already used | Run sudo ./install.sh upgrade from the package of the newer version, or restore a backup taken with the older one. See Downgrading. |
License
When a license is not valid, the License page explains why, and the app log records the reason.
| Symptom | Cause | Fix |
|---|---|---|
After sign-in, every page redirects to /license | No license is installed, or the license has expired and its grace period has ended | An administrator installs a license or a renewal. See Licensing. |
The license is invalid, with the reason instance_mismatch | The token was issued for a different installation, for example before a reinstall or a move to a new server | Request a new license for the instance ID shown on the License page at vulnsy.com/offline-licensing, and install it |
The license is invalid, with the reason clock_rollback | The server clock was set back by more than a day | Correct the system time, and keep it synchronized with NTP. The license is valid again once the clock is right. |
| A pasted token is rejected | The token was changed when it was copied, for example cut short or split across lines | Copy the whole token again, exactly as you received it |
The license shows as not installed although VULNSY_LICENSE_FILE is set | The file is not mounted into the app container, or the app's user (UID 1001) cannot read it | Check the volume mount and the file's permissions. See Install a License. |
Files and Downloads
The app log line starting with S3 storage: shows the storage endpoint and the URL used for download links.
| Symptom | Cause | Fix |
|---|---|---|
| Evidence images do not load and downloads fail | The browser cannot reach the files host: its DNS record is missing, or the proxy does not forward it to MinIO | Check that FILES_HOST resolves to the server and that the proxy forwards it to MinIO (127.0.0.1:9000 with your own proxy) |
| The browser shows a certificate error for the files host | There is no trusted certificate for FILES_HOST. Caddy could not obtain one (DNS, or ports 80 and 443 not reachable), a tls line was enabled in only one site block, your own certificate does not cover the files host, or users do not trust Caddy's root certificate (tls internal). | Check docker compose logs caddy and fix the cause. See Install. |
A download returns an XML error such as SignatureDoesNotMatch | The proxy changed the Host header, or serves the files host under a path prefix, so the request no longer matches its signature | Forward the files host at its root, and pass the Host header through unchanged. S3_PUBLIC_ENDPOINT must be exactly https:// followed by FILES_HOST. |
Download links point at minio:9000 or another internal address | S3_PUBLIC_ENDPOINT is empty | Set S3_PUBLIC_ENDPOINT=https://${FILES_HOST} in .env and run docker compose up -d app |
| Large uploads fail | The proxy in front of the app host limits request bodies to less than 50 MB | Allow request bodies of at least 50 MB |
The app log line starting with Email: shows the SMTP server, port and TLS mode in use. Failed sends are also logged.
| Symptom | Cause | Fix |
|---|---|---|
| No email arrives at all | SMTP_HOST is empty, so Vulnsy sends no email. The Email: log line reports that no transport is configured. | Set the SMTP_* variables and run docker compose up -d app |
| Sending fails with connection, timeout or TLS errors | The port and the TLS mode do not match. Port 465 needs SMTP_SECURE=true; ports 587 and 25 need SMTP_SECURE=false. | Correct SMTP_PORT or SMTP_SECURE |
| The mail server rejects the sender or the login | EMAIL_FROM is empty or is not an address the server lets you send from (an empty value falls back to a vulnsy.com address), or the credentials are wrong | Set EMAIL_FROM to an address your server accepts, and check SMTP_USER and SMTP_PASSWORD. For a relay without authentication, leave both empty. |
| Client portal emails go through a different server | An SMTP server is set up under Admin > Email > Delivery, and it takes precedence for client emails | Change or turn off that setting |
Emails sent to clients are also listed under Admin > Email > Logs.
Sign-in
| Symptom | Cause | Fix |
|---|---|---|
The password from INITIAL_ADMIN_PASSWORD is rejected | INITIAL_ADMIN_PASSWORD is read only on the first start. After the first sign-in, the password you chose replaced it, and editing the variable later has no effect. | Sign in with the password you chose, or have it reset as in the next row |
| An administrator has forgotten their password | Vulnsy stores only password hashes, so a password can be reset but not recovered | Another administrator opens Admin > Users & Access, edits the account and enters a new password. The user must change it at the next sign-in. If email works, Forgot password? on the sign-in page emails a temporary password instead. |
| No administrator can sign in | No account with the Admin role is available to reset the others | Contact Vulnsy support |
Forgot password? replaces the current password immediately, and sends the temporary one by email only. Use it only when email delivery works. Otherwise the user is left without a working password until an administrator sets a new one.
AI Assistant
| Symptom | Cause | Fix |
|---|---|---|
An endpoint is rejected with Endpoint must use HTTPS or Endpoint must be served on port 443 or 8443 | By default, an endpoint must use HTTPS on port 443 or 8443, at a public address | Serve the model over HTTPS. For a model server on your own network, set AI_ALLOW_PRIVATE_ENDPOINTS=true and run docker compose up -d app. |
An endpoint is rejected because it resolves to a private or reserved address | The endpoint is on a private network, and AI_ALLOW_PRIVATE_ENDPOINTS is not true | Set AI_ALLOW_PRIVATE_ENDPOINTS=true and run docker compose up -d app |
An endpoint is rejected because it resolves to a loopback or link-local address | The URL uses localhost, 127.0.0.1 or another loopback or link-local address. These are always blocked: inside the container, localhost is the app itself. | Use the server's LAN address or a Compose service name. See AI Options. |
| The AI assistant is not available | The license does not include the AI assistant, or AI is turned off | Check the License page. An administrator turns AI on under Admin > Organization > AI Assistant. |
Contacting Support
When you contact Vulnsy support, include the output of /api/version and the relevant log lines. Never send .env or a database dump: they contain your secrets and your data.