Self-Hosting

Configuration Reference

Configuration Reference

Everything is configured through environment variables. They come from the .env file in your install directory, which docker-compose.yml loads via env_file. Variables marked compose only are consumed by the compose file itself and never reach the application.

Edit .env, then apply:

cd docu.nexus
$EDITOR .env
docker compose up -d

Compose recreates only the containers whose configuration changed.

Core

VariableWhat it doesDefault
NODE_ENVproduction serves the built SPA from disk and marks session cookies Secure. development enables pretty logs.development
PORTHTTP API port inside the container.3000
COLLAB_PORTHocuspocus WebSocket port inside the container.3001
APP_URLPublic origin. Used for OAuth callback URLs and as the CORS fallback. No trailing slash.http://localhost:3000
CORS_ORIGINAllowed browser origin. Falls back to APP_URL, then to http://localhost:5173.unset
LOG_LEVELpino level: trace, debug, info, warn, error, fatal.info
WEB_DIST_PATHWhere the built SPA lives. Only read when NODE_ENV=production../apps/web/dist
COOKIE_SECUREWhether session cookies are marked Secure. A browser refuses to store a Secure cookie delivered over plain http, so an instance without TLS must set this to false or sign-in succeeds and every request after it is a 401. The installer sets it for you when APP_URL is http://. Remove it once TLS is in front. Unrecognised values are rejected rather than quietly treated as false.follows NODE_ENV (secure in production)
APP_VERSIONThe release this build is. Baked into the image at build time, so you do not set it. It is listed here because it is what Admin → About reports and what a support conversation asks for.the release tag, or the commit it was built from
DOCUNEXUS_UPDATE_CHECK_URLWhere Check for updates reads the current release number. Set it to empty to switch update checking off entirely, and the control disappears. Read only when an administrator presses the button; an instance never contacts it on its own.https://docu.nexus/dl/latest
APP_IMAGECompose only. Image to run. The installer rewrites this to whatever it loaded.the image the installer loaded
COMPOSE_PROJECT_NAMECompose only. Owns your data. Compose scopes every named volume by project, so this value is what makes postgres_data resolve to your database. Changing it on a running install silently points the stack at a new, empty one. Set once by the installer; never change it.docunexus
HOST_PORTCompose only. Host port mapped to container 3000. Your reverse proxy’s main upstream must point at it.3000
HOST_COLLAB_PORTCompose only. Host port mapped to container 3001. Your reverse proxy’s /collab route must reach it.3001

Database

VariableWhat it doesDefault
DATABASE_URLConnection string. The production compose builds it from DB_PASSWORD.postgres://docunexus:docunexus@localhost:5433/docunexus
DB_PASSWORDCompose only. Password for the docunexus Postgres role. Used by both containers.none, you must set it
DB_PROVIDERForces the dialect when the URL scheme is ambiguous: postgres, mysql, mssql.derived from the URL scheme
DB_POOL_MAXMaximum pooled connections.10
REDIS_URLRedis connection for the job queue.redis://localhost:6380
MIGRATE_ENABLEDRun pending migrations at container start. Any value other than true skips them.true
SEED_ENABLEDSeed demo data at container start. Pinned to false in docker-compose.yml, which outranks .env, so you cannot switch it on from there and should not try. The account it creates has a published password.false, pinned
SEED_EXAMPLE_CONTENTSeed the example content nexus at container start. Separate from SEED_ENABLED: it creates no accounts, and it stands down once an operator declines example content in the setup screen. Set to false for an empty instance.true
MIGRATIONS_PATHNothing. The migrations are compiled into the runner, so there is no directory to point it at. Kept in this table because setting it is harmless and the runner says once at start that it ignored it.unset

Authentication

VariableWhat it doesDefault
JWT_SECRETSigns session tokens. Changing it logs everyone out.change-me-in-production
ALLOW_REGISTRATIONPins self-signup at deployment level. Setting it at all makes the deployment own the answer, and Admin, then Authentication shows registration read-only. Only the exact string true enables signup; anything else pins it off. Leave it unset to manage registration from the admin panel.unset, admin panel decides (signup off by default)
OAUTH_GITLAB_CLIENT_IDGitLab application ID. GitLab sign-in is set up in Admin, then Authentication, then Single sign-on; this and the next three are read once, at the first start where that screen has no GitLab settings and both the ID and the secret are set, and are imported into it. After that the screen is in force and these are ignored.unset
OAUTH_GITLAB_CLIENT_SECRETGitLab application secret. Imported once, as above.unset
OAUTH_GITLAB_URLGitLab base URL for self-hosted instances. Imported once, as above; until then it also names the GitLab a Kanban board with no connection reaches.https://gitlab.com
OAUTH_GITLAB_SYNC_GROUP_IDGroup for member sync. Imported once, as above.unset
OAUTH_GITLAB_SYNC_TOKENA shared GitLab access token. The member sync uses it when the administrator has no GitLab sign-in token, and a Kanban board with no connection uses it for anybody who has not signed in with GitLab, so GitLab sees one shared account and everybody sees the same issues. Boards move cards and edit issues, so it needs the api scope; with read_api every change on a board is refused. Still read from the environment, and the member sync and Kanban only send it to the GitLab OAUTH_GITLAB_URL names.unset
OAUTH_MICROSOFT_CLIENT_IDEnables “Sign in with Microsoft” when set together with the secret.unset
OAUTH_MICROSOFT_CLIENT_SECRETAzure AD client secret.unset
OAUTH_MICROSOFT_TENANT_IDDirectory tenant. Use common for any Microsoft account.common

Instance administrators

The instance administrator is the account that answers for the whole deployment: licensing, the directory connections, and the list of who else is an instance administrator. It is not a nexus role. These three variables are the deployment’s own say in who holds it. The last two, while set, outrank what Admin, then Instance Administrators stores.

VariableWhat it doesDefault
INSTANCE_ADMIN_EMAILSAddresses made instance administrator at every start and every sign-in. Separated by new lines, semicolons or commas. The account must already exist, and the grant is kept when the line is removed. This is the lock-out recovery lever; see Nobody can administer.unset
INSTANCE_ADMIN_GROUPSGroups whose members hold instance administrator, checked at every sign-in and withdrawn when the membership goes. A directory group is its full DN. Separated by new lines or semicolons, never commas: a comma is part of a DN. While it is set, the panel shows the list read only.unset
INSTANCE_ADMIN_LDAP_CONNECTIONThe id of the one directory connection whose groups may grant instance administrator. Groups reported through any other connection are ignored, and with none named no directory grants anybody. It lets nobody sign in; that is each connection’s Use for sign-in switch.unset

A list that arrives in INSTANCE_ADMIN_GROUPS as cn=admins, ou=groups, dc=example has been split on its commas and matches nobody. Write each DN whole, and separate them with semicolons:

INSTANCE_ADMIN_GROUPS="CN=Docs Admins,OU=Groups,DC=corp,DC=example;CN=IT,OU=Groups,DC=corp,DC=example"

To find a connection’s id, run this from your install directory:

docker compose exec -T postgres psql -U docunexus -d docunexus -c \
  "SELECT id, name, url FROM ldap_configurations;"

LDAP

These variables do not switch LDAP on, and they do not change a saved connection. They fill in the form for a new connection at Admin, then Authentication, then LDAP, and nothing else. Sign-in reads what that form saves, and directory sign-in needs a saved connection with Use for sign-in on and the LDAP / Active Directory switch on the Sign-in methods tab of the same section. Set them when you deploy from configuration management and want the values already typed in for whoever finishes the setup. See LDAP and Active Directory Setup.

VariableWhat it doesDefault
LDAP_ENABLEDNothing. Kept only so that setting it makes the form say it was prefilled.false
LDAP_URLDirectory URL, read into the form’s Transport, Host and Port.ldap://localhost:389
LDAP_STARTTLSMakes the transport StartTLS. Never with an ldaps:// URL: the form then opens with no transport chosen, and will not save until one is.false
LDAP_TLS_REJECT_UNAUTHORIZEDVerify the directory’s certificate. Set false only for self-signed test rigs.enabled
LDAP_BIND_DNService account DN.unset
LDAP_SEARCH_BASEBase DN for user lookup.dc=example,dc=com
LDAP_SEARCH_FILTERUser filter template.built-in default
LDAP_SEARCH_SCOPEbase, one, or sub.sub
LDAP_USER_BIND_DOMAINAppended to the username for direct bind (Active Directory style).unset
LDAP_USERNAME_ATTRIBUTEUsername attribute.uid
LDAP_EMAIL_ATTRIBUTEEmail attribute.mail
LDAP_DISPLAY_NAME_ATTRIBUTEDisplay name attribute.cn
LDAP_FIRST_NAME_ATTRIBUTEGiven name attribute.givenName
LDAP_LAST_NAME_ATTRIBUTESurname attribute.sn
LDAP_GROUP_ATTRIBUTEGroup membership attribute.memberOf
LDAP_AUTO_PROVISIONCreate accounts on first sign-in. Set false to disable.enabled
LDAP_SYNC_ON_LOGINRefresh attributes on each sign-in. Set false to disable.enabled

There is no variable for the bind password, the CA certificate or the connection name. Type those into the form, where a save checks a new bind password against the directory before storing it.

Retired and deprecated

The app writes a line to its log at every start while one of these is set, so docker compose logs app | grep -i ldap shows whether yours still are.

VariableStatus
LDAP_BIND_CREDENTIALSRetired, does nothing. It filled in the bind password of the next new connection, whatever host that connection named.
LDAP_ADMIN_GROUPSRetired, does nothing. A directory no longer makes anybody a nexus admin. For instance administrators, use INSTANCE_ADMIN_GROUPS above.
LDAP_DEFAULT_ROLERetired, does nothing. A directory no longer places anybody into a nexus; an admin of each nexus picks the role when they place a person.
LDAP_SIGNIN_CONNECTIONSDeprecated, and removed in a later release. While set it can only narrow sign-in: a connection whose id it does not name signs nobody in, whatever its Use for sign-in switch says. It never switches one on. Ids are separated by spaces, commas or semicolons. Keep it unchanged until the release that removes it: rolling back to the previous version restores the database but not .env, and that version reads this variable first. See Rolling back.

Collaboration

VariableWhat it doesDefault
ENABLE_COLLABORATIONSet to false to skip starting the Hocuspocus server. Any other value starts it. Without it, pages cannot be edited or published from the editor.enabled

Confluence

The Confluence surface is off by default. Import, export, the webhook receiver, sync, and the background sync scheduler are not started, the /api/confluence/* endpoints answer 404, and the Import and Sync sections do not appear in the Admin panel. It ships off because the export half of the surface is not finished: those endpoints check that a caller is signed in and stop there, without checking whether that account may read the page or the space. Switching it on is a decision to accept that on an instance where every signed-in account is already trusted with every page.

One line in .env turns the whole subject back on, then restart:

ENABLE_CONFLUENCE=true
VariableWhat it doesDefault
ENABLE_CONFLUENCEThe whole Confluence subject: import, export, webhook, sync, and the sync scheduler. Only true, 1, yes, or on switch it on; anything else, including a misspelt value and an empty one, leaves it off.off
ENABLE_CONFLUENCE_SYNCNarrower switch, and it only matters when ENABLE_CONFLUENCE is on: set it to false to keep the surface but skip the background sync scheduler. It cannot switch anything on by itself.enabled, but inert while ENABLE_CONFLUENCE is off
SYNC_CHECK_INTERVALSync poll interval in milliseconds. Read only when the scheduler runs.60000

Nothing stored is lost while it is off. Saved connections, sync configurations, and past import jobs stay in the database and come back exactly as they were.

Storage

VariableWhat it doesDefault
STORAGE_PATHRoot directory for uploads. Images land in uploads/YYYY/MM, attachments in files/YYYY/MM../storage

The production compose sets STORAGE_PATH=/app/storage and backs it with the app_storage volume. Set it explicitly. A few Confluence import paths fall back to ./uploads when it is unset, which splits your files across two directories.

Build-time front end

These are baked into the SPA bundle at build time, not read at runtime.

VariableWhat it doesDefault
VITE_BASE_URLAbsolute API base. Leave empty when the proxy serves the API and the SPA on one origin.empty (same origin)
VITE_COLLAB_URLExplicit WebSocket URL. Overrides the automatic wss://<host>/collab behavior.unset

Git Docs

All optional; the plugin works with none of them set. Repo clones are cached under STORAGE_PATH/gitdocs/.

VariableWhat it doesDefault
GITDOCS_PUBLIC_URLPublic base for the webhook payload / artifact upload URLs the setup form shows. Only needed when it differs from APP_URL.falls back to APP_URL
GITDOCS_ALLOWED_HOST_PATTERNRegex of additionally allowed clone hosts. Private, loopback, and link-local hosts are refused unless they match (SSRF guard). Set this for an internal git server, e.g. ^git\.internal\.example\.com$.unset
GITDOCS_CLONE_TIMEOUT_MSTimeout for any git subprocess.120000
GITDOCS_MAX_FILE_MBLargest repo file the plugin will read and render.5
GITDOCS_ENABLE_POLLPoll repos for changes as a fallback where webhooks are not possible. Must be the exact string true.disabled
GITDOCS_POLL_INTERVAL_MINPoll interval in minutes.15
GITDOCS_ARTIFACT_MAX_MBSize cap for one uploaded artifact site.200
GITDOCS_ARTIFACT_MAX_FILESFile-count cap for one uploaded artifact site.10000
GITDOCS_ARTIFACT_KEEPArtifact versions kept per page; older ones are evicted on upload.5
GITDOCS_ARTIFACT_HOSTWildcard domain artifact sites are served from, one hostname per version. Set it and sites that reference /css/... work unmodified. Needs a wildcard DNS record and certificate; see Git Docs.unset

Licensing

VariableWhat it doesDefault
DOCUNEXUS_LICENSE_KEYPins the instance license at the deployment level. When set, it takes precedence over any key pasted in Admin, and the Admin UI refuses to overwrite it.unset (the key comes from the database; with none, the instance evaluates and then becomes read-only)

Set this if you manage secrets through your orchestrator. Leave it unset if you prefer pasting the key once in Admin, Licensing.