Skip to content

OnlyOffice

OnlyOffice Document Server is a shared editing backend — it provides browser-based Office document editing for any application that integrates with it, such as Seafile Pro or Nextcloud. It runs as a single container and serves an editor that embeds as an iframe inside the consuming application.

Last run on a host 8 Nov 2026

Pinned at 10.0.0 (released 12 Oct 2026) · the newest upstream release

In the upstream images: 0 CRITICAL · 2 HIGH with a published fix — per image

One OnlyOffice instance can serve multiple consuming applications simultaneously. Each application must hold the same JWT secret and have its origin listed in the allowed-origins setting.

To try it: Docker, and a JWT secret, which the editor requires either way. See Try it locally.

To deploy it behind the proxy: Traefik running, a hostname, a JWT secret, and the origin(s) of each application that will embed the editor.

On your own machine, with no proxy, domain or certificate. From apps/onlyoffice/:

Terminal window
cp .env.local.example .env.local
# The secrets ship empty; generate the ones this stack needs
echo "JWT_SECRET=$(openssl rand -base64 30 | tr -d '\n')" >> .env.local
docker compose -f docker-compose.local.yml --env-file .env.local up -d

Then open http://localhost:8080. Stop it again with the same command and down.

From apps/onlyoffice/:

  1. Terminal window
    cp .env.example .env
  2. Set the hostname and timezone in .env:

    APP_TRAEFIK_HOST=office.example.com
    TZ=Europe/Vienna
  3. Set ONLYOFFICE_ALLOWED_ORIGINS to the hostname(s) of each application that will embed the editor — space-separated, with https:// prefix:

    ONLYOFFICE_ALLOWED_ORIGINS=https://files.example.com
  4. Generate the JWT secret:

    Terminal window
    mkdir -p .secrets
    openssl rand -base64 30 | tr -d '\n' > .secrets/jwt_secret.txt
  5. Start:

    Terminal window
    docker compose up -d
  6. First boot takes roughly 90 seconds — PostgreSQL initialises and the document server warms up:

    Terminal window
    docker compose logs onlyoffice-app --follow
    # Wait for: ONLYOFFICE Document Server ... is up and running

After OnlyOffice is running, share the JWT secret with each consuming application. The exact step depends on the app — for example, Seafile Pro reads it from .env rather than a secrets file. See the consuming application’s guide for the specific step.

Terminal window
curl -fsSI https://your-domain/healthcheck # expect 200 OK
curl -fsSI https://your-domain/web-apps/apps/api/documents/api.js # expect 200 OK

Full functional verification requires a consuming application — open a .docx file in Seafile Pro (or another connected app) and confirm the editor loads in the browser without X-Frame-Options or CSP errors in the browser console.

ONLYOFFICE_ALLOWED_ORIGINS controls which application origins browsers are permitted to embed the editor from. It maps to a CSP frame-ancestors directive — any origin not listed is rejected by the browser even if the JWT is valid.

Changes to ONLYOFFICE_ALLOWED_ORIGINS require container recreation, not just a restart. The value is embedded in a Traefik label at container creation time:

Terminal window
# After changing ONLYOFFICE_ALLOWED_ORIGINS in .env:
docker compose up -d --force-recreate

A plain docker compose restart leaves the old CSP in place and the browser will continue to block embedding from any origin not in the original value.

OnlyOffice fetches each document from the consuming application and saves it back through a callback. The consuming applications ship closed routes, and a closed route refuses every container (DEC-076), so on the same host these calls go past the route:

  • Nextcloud — its opt-in office.yml joins Nextcloud’s nginx to this stack’s edge network; the connector hands out nginx as the storage address and needs this server’s secret as its setting jwt_secret (apps/nextcloud/README.md, “Office servers behind a closed route”).
  • Seafile CE and Pro — their opt-in onlyoffice-callback.yml opens the two paths OnlyOffice uses.

An OnlyOffice server on another host reaches a closed consuming application only by its address in TRUSTED_RANGES. DOCX, XLSX and PPTX open, save and reopen through Nextcloud and through Seafile CE and Pro this way; the private-address filter lets the JWT-signed storage URL through without ALLOW_PRIVATE_IP_ADDRESS.

Add each application’s origin to ONLYOFFICE_ALLOWED_ORIGINS, space-separated:

ONLYOFFICE_ALLOWED_ORIGINS=https://files.example.com https://cloud.example.com

Each application also needs a copy of the same JWT secret. Mismatched secrets produce Token is invalid errors in the browser console.

After adding a new origin, recreate the container to apply the updated CSP:

Terminal window
docker compose up -d --force-recreate

Set APP_TRAEFIK_ACCESS=access-vpn in .env to admit only VPN clients to the OnlyOffice server. The browser loads the editor from this route, so every user then needs the VPN. Nextcloud also calls the document server at its public name from its own container, and a closed route refuses every container (DEC-076): with access-vpn that call fails and the Nextcloud integration stops working.

Rotating the JWT secret requires updating both sides simultaneously — there is no downtime-free path:

  1. Update .secrets/jwt_secret.txt on the OnlyOffice server.
  2. Update the secret in every consuming application (check each app’s guide for where it is stored).
  3. Restart OnlyOffice and each consuming application.

Active editing sessions will break when the secret changes.

Database None in this stack.
State .secrets/jwt_secret.txt — shared with the embedding application
Rebuildable volumes/data (document cache) · volumes/logs
Quiescing Not applicable.

Documents live in the application that embeds this document server and are backed up there; what is cached here is a working copy. The JWT secret is the one thing whose loss is felt: both sides hold the same value, so regenerating it means updating every embedding application too.

  1. Read the release notes.

  2. If people are editing, let their sessions close first — up to five minutes:

    Terminal window
    docker exec onlyoffice-app documentserver-prepare4shutdown
  3. Raise APP_TAG in .env, then:

    Terminal window
    docker compose pull && docker compose up -d
    docker compose logs onlyoffice-app --follow

    The first start takes one to five minutes; wait for Document Server … is up and running.

  4. Check health, then open a .docx from each embedding application, save it and reopen it:

    Terminal window
    curl -fsSI https://<APP_TRAEFIK_HOST>/healthcheck # 200
    curl -fsSI https://<APP_TRAEFIK_HOST>/web-apps/apps/api/documents/api.js # 200

Editor fails to load or shows a blank area: open the browser console and look for a CSP or X-Frame-Options error. The most likely cause is the consuming application’s origin missing from ONLYOFFICE_ALLOWED_ORIGINS. Confirm the value matches exactly, including the https:// prefix, then recreate the OnlyOffice container.

Document does not load or changes do not save: OnlyOffice cannot reach the consuming application server-to-server. Check:

  • DNS resolution for the consuming application’s hostname from the OnlyOffice host.
  • Whether the consuming application’s Traefik access middleware is blocking the request.
  • That the JWT secret in the consuming application matches the OnlyOffice secret exactly.

Allowed-origins change has no effect: ONLYOFFICE_ALLOWED_ORIGINS is baked into a Traefik label at container creation time, so docker compose restart does not update it. Run docker compose up -d --force-recreate on the OnlyOffice server.

A request is rejected although the consuming application uses access-vpn: the source IP Traefik sees may not match the expected Tailscale address — see Traefik troubleshooting for the Docker bridge source-IP caveat.

Private or Tailscale path: document does not load: verify that DNS for the consuming application’s hostname resolves to an address reachable from the OnlyOffice host. If it resolves to a public IP blocked by a firewall, the server-to-server fetch fails, and the browser console shows no clear error.