Self-Hosting

Troubleshooting

Troubleshooting

Every command on this page is run from your install directory — the one holding docker-compose.yml and .env.

cd docu.nexus

Is it alive

curl -f http://localhost:3000/health

Returns {"status":"ok","timestamp":"..."}. The container has its own healthcheck hitting the same endpoint every 30 seconds, so docker compose ps reflects it too.

docker compose ps

Logs

# everything, following
docker compose logs -f

# just the app
docker compose logs -f app

# last 200 lines from Postgres
docker compose logs --tail 200 postgres

Service names are app, postgres, and redis. Container names are docunexus-app, docunexus-postgres, and docunexus-redis, so docker logs docunexus-app works too.

Raise detail with LOG_LEVEL=debug in .env, then restart the app container.

Common failures

ProblemCauseFix
bind: address already in use on startSomething else on the host holds 3000 or 3001.lsof -i :3000 to find it. Or set HOST_PORT and HOST_COLLAB_PORT in .env to remap. The app still listens on 3000 and 3001 inside the container.
Cannot connect to the Docker daemonDocker is not running.Start Docker Desktop or systemctl start docker.
App container hangs at “Waiting for PostgreSQL to be ready…”Postgres never became healthy, or DATABASE_URL points at the wrong host.docker compose logs postgres. Inside the compose network the host is postgres, not localhost.
Container exits right after “Running database migrations…”A migration failed. The entrypoint aborts rather than serving a half-migrated schema.Read the migration name in the log. Restore your pre-upgrade dump, then investigate. See Upgrading.
password authentication failed for user "docunexus"DB_PASSWORD in .env changed after the database volume was created. Postgres reads POSTGRES_PASSWORD only when it first initialises, so the new value changed what the app sends and not what the database expects.Put the old value back, or change it inside Postgres to match: docker compose exec postgres psql -U docunexus -c "ALTER ROLE docunexus PASSWORD 'the new one';". Never destroy the volume to fix this — it is your entire database.
Sign-in works, then every request is a 401The site is served over plain http and session cookies are marked Secure, so the browser accepted the login and threw the cookie away.Put TLS in front, or set COOKIE_SECURE=false in .env and docker compose up -d. See Production Deployment.
Send feedback has no “Attach a screenshot of this page” checkbox, and the panel says the connection needs httpsThe site is served over plain http. Browsers expose the screen-capture API only to a secure context, so on that origin it is absent rather than denied, and the app says so instead of attaching an approximation of the page.Put TLS in front. Until then, take a screenshot with your own operating system and use Upload your own image in the same panel, which is not a lesser report. localhost counts as secure, so the checkbox is there if you open the instance on the host itself.
Send feedback has no checkbox on an iPad, iPhone or Android phone, including over httpsNo mobile browser can capture the screen. Every browser on iOS and iPadOS is WebKit, whatever its name and icon, and WebKit does not implement the screen-capture API there; Chrome on Android does not either. The panel names the platform rather than the connection in this case, so nothing here is misconfigured.Take a screenshot the way the device does, then use Upload your own image. A report with one of those attached is worth exactly as much as a captured one.
Fresh, empty instance after a restart, and the old data is “gone”COMPOSE_PROJECT_NAME changed, or the stack was started from a different directory. Compose scopes volumes by project, so the stack attached to a new, empty volume and migrations made it look healthy.Nothing was deleted. `docker volume ls
exec format error when the app container startsThe loaded image is built for a different CPU architecture than this host.uname -m on the host, then download the matching build from https://docu.nexus/dl/. See Upgrading.
Editor shows no other cursors, no presenceThe /collab/ proxy location is missing, so the WebSocket never connects.Add the /collab/ block from Production Deployment. Check the browser console for [Collab] Connecting to: and see where it is aiming. Override with VITE_COLLAB_URL at build time if your topology differs.
Collaboration works locally, dies behind the proxyThe proxy is not forwarding Upgrade and Connection headers, or it is timing the socket out.Set proxy_http_version 1.1, both upgrade headers, and proxy_read_timeout 86400.
Uploads fail with a write errorThe app runs as the non-root docunexus user. Anything it cannot write to /app/storage fails.Use the app_storage volume from the production compose. If you bind-mount a host directory instead, chown it to the container’s uid.
Uploads fail on large files onlyThe reverse proxy is rejecting the body before the app sees it.Raise client_max_body_size in nginx. The shipped config uses 100M.
Attachments 404 after a restoreYou restored the database but not the storage volume.Restore app_storage too. See Backup and Restore.
Everyone logged out after a restartJWT_SECRET changed. Tokens signed with the old secret no longer verify.Put the original secret back, or accept it and let people log in again.
Sign up returns a permission errorOne of three things: ALLOW_REGISTRATION is set to something other than true, or self-registration is off in the admin panel, or email and password sign-in is switched off as a mode altogether.The error text tells you which. Unset ALLOW_REGISTRATION to hand the decision back to the panel, then check Let anyone create their own account and the Email and password switch under Admin, then Authentication, then Sign-in methods. Or invite users from Admin, Members instead.
Login rejected despite a correct passwordEmail addresses are stored lowercase. A mixed-case entry from an external directory will not match.Log in with the lowercase form.
No password form on the login page, and the directory tab does not work eitherEmail and password sign-in is switched off, and the directory it was switched off in favour of is unreachable.Nobody can sign in, below.
A paid plugin reports “not entitled”No valid license key is loaded.Paste the key in Admin, Licensing, or set DOCUNEXUS_LICENSE_KEY and restart. The env variable wins and locks the Admin field.

Nobody can sign in

The shape of this: email and password sign-in is switched off because everyone uses the directory, and then the directory stops answering. It might have been unplugged, moved, or never have resolved from the server in the first place. The login page offers a directory tab that fails and no password form, and the admin panel that could switch password sign-in back on is behind the login page.

docu.nexus refuses a change that would leave no configured sign-in method, but “configured” means a connection is saved, not that anything answers it. Nothing can refuse a directory that stops working an hour after you saved it, so the way out is a command on the server rather than a guarantee in the product.

There is no page and no endpoint that does this, which is deliberate: an endpoint that re-enables password sign-in is an endpoint worth attacking. Recovery is a command on the server, and shell access to the server is the credential.

Run it from your install directory. It writes one key in the settings singleton — the same key the admin panel writes — and turns the email and password form back on:

docker compose exec -T postgres psql -U docunexus -d docunexus -c \
  "UPDATE instance_settings
      SET settings = jsonb_set(
            COALESCE(settings, '{}'::jsonb),
            '{auth,local,enabled}', 'true'::jsonb, true)
    WHERE id = 'singleton';"

It should print UPDATE 1. UPDATE 0 means the settings row does not exist yet, which on an instance that has had LDAP configured should not happen — stop there and contact support rather than creating one by hand.

Then reload the login page; no restart is needed, because sign-in modes are read from the database on every request. The password form is back, and any account that has a password can use it.

To see which accounts that is:

docker compose exec -T postgres psql -U docunexus -d docunexus -c \
  "SELECT email FROM users WHERE password_hash IS NOT NULL AND deactivated_at IS NULL;"

Accounts created by LDAP or by an SSO provider have no password at all, so an instance where every account came from the directory will list nothing here. Setting a password from the database is not something you can do by hand — the hash is argon2 and the database has no way to compute one — so that case needs support. Say that you have re-enabled local sign-in and that no account has a password; it is a short fix on our side, not a lost instance.

Once you are in, fix the directory or switch the LDAP mode off under Admin, then Authentication.

Nobody can administer

Different problem, easier answer. Sign-in works, but the last account with instance-administrator rights left, was deactivated, or lost the rights to a directory sync.

Add the address to .env and restart. The grant lands at boot and at every sign-in. The account must already exist: while an address is listed, nothing in the product will create an account for it.

cd docu.nexus
echo 'INSTANCE_ADMIN_EMAILS=owner@example.com' >> .env
docker compose up -d app
docker compose logs app | grep instance-admin

The grant is sticky, so you can take the line back out afterwards and it revokes nothing.

On an instance with no accounts at all, neither of these applies: the first-run setup wizard is still available and is the way in.