Skip to content

How to Deploy ONLYOFFICE Docs on a VPS

Updated Oct 2026

verified on Ubuntu 26.04 · Oct 2026
We earn commissions when you shop through the links below. Full disclosure →

Self-host ONLYOFFICE Docs (Document Server) — the browser editor for Word, Excel and PowerPoint files — on a VPS with Docker Compose, JWT protection and Caddy HTTPS, ready to connect to Nextcloud or another file platform.

Before you start
  • A VPS with 4 GB+ RAM (the catalog's floor for ONLYOFFICE Docs; the bundled database, message queue and conversion services all run in one container)
  • A fresh Ubuntu 24.04 or 26.04 server with root/sudo SSH access
  • A domain or subdomain you can point at the server, separate from your file platform's
Need a box for this guide? Kamatera's free tier lets you spin one up now.Start free on Kamatera → (opens in new tab)

What ONLYOFFICE Docs is

ONLYOFFICE Docs (the product's installable server is called Document Server) is a web-based editor for text documents, spreadsheets, presentations and PDFs. It works natively on the Office Open XML formats (DOCX, XLSX, PPTX) and supports real-time co-editing. It is rated 2 / 5 to deploy and the Community Edition is licensed under the AGPL-3.0. If you want a self-hosted alternative to Microsoft 365 or Google Workspace editors, this is the engine.

The thing to understand first: ONLYOFFICE Docs has no file manager, no login and no storage of its own. It is an editing service. A file platform such as Nextcloud, Seafile or ownCloud holds your documents and, when someone opens one, asks Document Server to render an editor for it. So you deploy this guide's server alongside a file platform, not instead of one. If you don't have one yet, start with Deploy Nextcloud on a VPS and come back.

This guide covers the Document Server half: install it, put it on HTTPS with a JWT secret, and prove the service is healthy. The connector setup on the file platform's side is described at the end, but was not tested here. For help choosing a provider and tier, see Best VPS for Nextcloud.

Server sizing

The catalog lists 4 GB of RAM as the floor for ONLYOFFICE Docs, and that is the number to plan around. At idle it is lighter: on the catalog's test box the container used about 454 MB of RAM and the whole install about 4.9 GB of disk, most of it the image. On our own verification run the container idled at about 500 MB before any document was opened. Idle is not the working figure. Every open editing session, every conversion and every spreadsheet recalculation costs memory, and the bundled services (a database, a message broker, a cache and the conversion workers) all share the one container.

If the Document Server runs on the same VPS as your file platform, add the two requirements together. On a 4 GB machine that leaves little headroom, so a separate server, or a larger tier, is usually the better choice for a team. ONLYOFFICE also publishes a limit on simultaneous editing connections for the Community Edition; check the current figure in its documentation before you size for a large group.

Prepare the server

Update the system and install the few tools this guide uses:

sudo apt-get update && sudo apt-get -y upgrade
sudo apt-get install -y curl openssl ufw dnsutils

If you're logged in as root, create a non-root sudo user and continue as that user:

adduser deploy
usermod -aG sudo deploy

Then open only SSH and the two ports the reverse proxy needs:

sudo ufw allow OpenSSH
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw --force enable

Document Server will be reachable only through the reverse proxy. That matters on a Docker host: a published container port goes around ufw entirely, so the Compose file below binds the app to 127.0.0.1, and Caddy becomes the only public door. (The catalog's one-line docker run publishes port 8080 on every interface; fine for a quick look, not for a server on the internet.)

Where to host itaffiliate disclosure
Hetzner Cloudrun it on
2 vCPU · 4 GB RAM · 80 GB SSD · $23.59/mo
Get Hetzner Cloud (opens in new tab)
Kamaterafree trial
1 vCPU · 1 GB RAM · 20 GB SSD · $4.00/mo
Start free on Kamatera → (opens in new tab)
DigitalOceanalso works on
1 vCPU · 1 GB RAM · 25 GB SSD · $6.00/mo
Deploy on DigitalOcean → (opens in new tab)

Paid link — we earn a commission if you shop through it.

Install Docker

If Docker isn't on the server yet, the convenience script installs Docker Engine and the Compose plugin (the Docker and Compose on Ubuntu guide covers the details):

command -v docker >/dev/null || curl -fsSL https://get.docker.com | sudo sh
sudo usermod -aG docker "$USER"
sudo docker compose version

The group change applies on your next login; until then the commands below use sudo docker.

Install ONLYOFFICE Docs

Create a project directory and generate the JWT secret. Every request between your file platform and Document Server is signed with this shared secret; without it, anyone who can reach the server could ask it to open any URL. The secret must stay fixed, because a container that generates its own will pick a new one on every restart and silently break every connector. Set your hostname first (this guide uses docs.example.com; replace it with yours). The guard keeps the step safe to re-run:

mkdir -p ~/onlyoffice && cd ~/onlyoffice
DOMAIN=docs.example.com
if [ ! -f .env ]; then
  ( umask 077
    {
      echo "DOMAIN=$DOMAIN"
      echo "JWT_SECRET=$(openssl rand -hex 32)"
    } > .env )
fi
grep -c -E '^(DOMAIN|JWT_SECRET)=' .env

Now the Compose file. It runs the official image with its four data directories on named volumes, the image tag pinned, the port bound to loopback and Caddy added in front:

cd ~/onlyoffice
cat > docker-compose.yml <<'EOF'
services:
  docs:
    image: onlyoffice/documentserver:9.4.0.1
    restart: always
    ports: ["127.0.0.1:8080:80"]
    environment:
      JWT_ENABLED: "true"
      JWT_SECRET: ${JWT_SECRET}
    volumes:
      - docs_logs:/var/log/onlyoffice
      - docs_data:/var/www/onlyoffice/Data
      - docs_lib:/var/lib/onlyoffice
      - docs_db:/var/lib/postgresql
  caddy:
    image: caddy:2
    restart: always
    ports: ["80:80", "443:443"]
    environment:
      DOMAIN: ${DOMAIN}
    volumes: ["./Caddyfile:/etc/caddy/Caddyfile:ro", "caddy_data:/data"]
    depends_on: [docs]
volumes:
  docs_logs:
  docs_data:
  docs_lib:
  docs_db:
  caddy_data:
EOF
cat > Caddyfile <<'EOF'
{$DOMAIN} {
    reverse_proxy docs:80
}
EOF

Start it and wait for the service. The first start initialises the database and generates fonts and the editor bundles, which takes a few minutes, so the loop below waits up to ten and then gives up rather than hanging:

cd ~/onlyoffice
sudo docker compose up -d
out=
for i in $(seq 1 120); do
  out=$(curl -s http://127.0.0.1:8080/healthcheck)
  [ "$out" = "true" ] && break
  sleep 5
done
echo "healthcheck says: $out"
sudo docker compose ps

healthcheck says: true means every internal service is up. Document Server answers its health endpoint only when the editors, the converter and the database are all running, so this is a stronger check than "the container started". If it never reports true, jump to Troubleshooting.

HTTPS + domain

Create an A record for your hostname (say docs.example.com) pointing at the server's public IP, and confirm it resolves before Caddy tries to get a certificate:

dig +short docs.example.com

Caddy is already running. It requests the certificate as soon as the name resolves and renews it on its own (see Automatic HTTPS with Caddy for how that works). Check that HTTPS answers, retrying while the certificate is issued:

. ~/onlyoffice/.env
out=000
for i in $(seq 1 20); do
  out=$(curl -s -o /dev/null -w '%{http_code} %{ssl_verify_result}' "https://$DOMAIN/healthcheck") && [ "${out%% *}" = "200" ] && break
  sleep 5
done
echo "HTTPS status and certificate check: $out"
curl -s "https://$DOMAIN/healthcheck"; echo

A second number of 0 means the certificate verified, and the last line should again print true. Your file platform must be able to reach this HTTPS address from its own server, and Document Server must be able to reach the file platform back, because it fetches the document from it and posts the saved version back when editing ends.

Check that the JWT secret is enforced

A Document Server without a working secret is an open proxy for rendering arbitrary files, so confirm the protection is on. The service accepts management commands at /command. A request with no token should be refused with an error code, not answered with the server version:

. ~/onlyoffice/.env
curl -s -X POST -H 'Content-Type: application/json' -d '{"c":"version"}' "https://$DOMAIN/command"; echo

A reply containing "error":6 means the request was rejected for a missing or invalid token, which is what you want; a reply with "error":0 and a version number means JWT is not being enforced and you should recheck JWT_ENABLED and the secret in .env.

Connect it to a file platform

The following steps happen on your file platform, not on this server, and were not tested in this guide's verification run (which covered Document Server alone). For Nextcloud:

  1. Install the ONLYOFFICE app from the Nextcloud app store (it is a connector, not the editor itself).
  2. In Administration settings → ONLYOFFICE, set the Document Editing Service address to https://docs.example.com/.
  3. Paste the JWT_SECRET value from ~/onlyoffice/.env into the Secret key field and save. Print it with grep JWT_SECRET ~/onlyoffice/.env.
  4. Open a DOCX file in Nextcloud. It should open in the ONLYOFFICE editor.

Seafile and ownCloud have equivalent connectors with the same two settings: the Document Server address and the shared secret. If the connector reports that it cannot reach the other side, the usual cause is a firewall or an internal-address restriction between two services on the same network; check the ONLYOFFICE documentation on connecting to servers with private addresses.

Backups

Document Server holds no documents. Your files live in the file platform, so that is where backups of your documents belong. What this server holds is its configuration (the .env with the JWT secret) and some regenerable state: the editor cache and an internal database of in-progress editing sessions. The irreplaceable piece is the secret, because losing it means reconfiguring every connector. Archive the configuration, restricted to its owner:

cd ~/onlyoffice
( umask 077; tar czf ~/onlyoffice-config-$(date +%F).tar.gz docker-compose.yml Caddyfile .env )
ls -lh ~/onlyoffice-config-*.tar.gz

Copy it off the server, to encrypted storage or a password manager. The archive contains the JWT secret, so handle it accordingly. A backup you have never restored is a guess, so prove this one unpacks into a working configuration. This block extracts the archive into a scratch directory and asks Docker Compose to validate the restored files:

rm -rf ~/onlyoffice-restore-test && mkdir ~/onlyoffice-restore-test
tar xzf ~/onlyoffice-config-$(date +%F).tar.gz -C ~/onlyoffice-restore-test
cd ~/onlyoffice-restore-test
sudo docker compose config -q && echo "restored configuration is valid" && grep -c '^JWT_SECRET=' .env
rc=$?
cd ~ && rm -rf ~/onlyoffice-restore-test
[ "$rc" -eq 0 ]

That proves the configuration archive is complete and readable; it does not test the volumes. If you also want the editor cache and database volumes, stop the stack and archive the onlyoffice_docs_data, onlyoffice_docs_lib and onlyoffice_docs_db volumes with a throwaway container, as in the other guides in this series; most deployments simply let Document Server rebuild them. To rebuild on a new server, install Docker, unpack the configuration archive into ~/onlyoffice and start the stack.

Upgrades

Because the image tag is pinned, an upgrade is an edit. Check ONLYOFFICE's release notes, take a backup (previous section), change the tag in docker-compose.yml, then pull and recreate:

cd ~/onlyoffice
sudo docker compose pull
sudo docker compose up -d
sudo docker compose logs --tail 20 docs

The first start after an upgrade regenerates the editor bundles and migrates the internal database, so allow a few minutes and wait for /healthcheck to answer true again. Upgrade the connector on the file platform around the same time; a very old connector with a new Document Server (or the reverse) is a common cause of "cannot open document" errors. Open a document from each connected platform before you call the upgrade done.

Troubleshooting

/healthcheck never says true. Give the first start the full ten minutes; it is slow on small servers. Then read sudo docker compose logs docs and free -m. An out-of-memory kill of one of the bundled services is the usual cause on a server under 4 GB.

Caddy returns 502. The Document Server container is still starting or has exited. Check sudo docker compose ps; Caddy only works once docs answers on its internal port.

The file platform says the editing service cannot be reached, or "invalid token". The address must be the public HTTPS address of this server, and the secret in the connector must match JWT_SECRET in .env exactly. If you recreated the container without a fixed secret, it changed; set it in .env, recreate, and update the connector.

Editors open but never save. Document Server cannot call back to your file platform, usually because of a firewall, a certificate it does not trust (a self-signed certificate on the file platform) or a private-address restriction. The logs in /var/log/onlyoffice (the docs_logs volume) name the failing URL.

Caddy never gets a certificate. Confirm dig +short docs.example.com returns the server's IP and that your provider's network-level firewall, not just ufw, allows inbound 80 and 443. A blocked port 80 silently breaks the ACME challenge.

Verification + next steps

You're done when https://docs.example.com/healthcheck returns true over a valid certificate, the unauthenticated /command request is refused with "error":6, docker compose ps shows both services up after a reboot (restart: always brings them back), and a DOCX file opens, edits and saves from your file platform.

From there, enable co-editing for a test folder, add any fonts your documents need, and decide whether Document Server should get its own server as usage grows. See the ONLYOFFICE entry for catalog details, Deploy Nextcloud on a VPS for a file platform to pair it with and Best VPS for Nextcloud for the ranked server picks.

Next steps

How to self-host ONLYOFFICE Docs →Automatic HTTPS with Caddy →Run Claude Code with Ollama on Your Own VPS →Deploy Coolify on a VPS →How to Deploy Actual Budget on a VPS →How to Deploy AFFiNE on a VPS →How to Deploy AnythingLLM on a VPS →How to Deploy Appwrite on a VPS →How to Deploy Audiobookshelf on a VPS →How to Deploy Authelia on a VPS →How to Deploy authentik on a VPS →How to Deploy Baserow on a VPS →How to Deploy Beszel on a VPS →How to Deploy Bitwarden on a VPS →How to Deploy BookStack on a VPS →How to Deploy CapRover on a VPS →How to Deploy Checkmate on a VPS →How to Deploy copyparty on a VPS →How to Deploy Directus on a VPS →How to Deploy Discourse on a VPS →How to Deploy docker-mailserver on a VPS →How to Deploy Docmost on a VPS →How to Deploy Dokku on a VPS →How to Deploy Dokploy on a VPS →How to Deploy Firefly III on a VPS →How to Deploy Flarum on a VPS →How to Deploy Forgejo on a VPS →How to Deploy Gatus on a VPS →How to Deploy Ghost on a VPS →How to Deploy Ghostfolio on a VPS →How to Deploy Gitea on a VPS →How to Deploy a GitHub Actions Self-Hosted Runner on a VPS →How to Deploy GitLab on a VPS →How to Deploy GlitchTip on a VPS →How to Deploy Grafana on a VPS →How to Deploy Graylog on a VPS →How to Deploy Headscale on a VPS →How to Deploy Healthchecks on a VPS →How to Deploy Home Assistant on a VPS →How to Deploy Immich on a VPS →How to Deploy Infisical on a VPS →How to Deploy Invoice Ninja on a VPS →How to Deploy Jan on a VPS →How to Deploy Jellyfin on a VPS →How to Deploy Kanboard on a VPS →How to Deploy Karakeep on a VPS →How to Deploy Keycloak on a VPS →How to Deploy Leantime on a VPS →How to Deploy LibreChat on a VPS →How to Deploy Linkwarden on a VPS →How to Deploy LocalAI on a VPS →How to Deploy Mailcow on a VPS →How to Deploy Mailu on a VPS →How to Deploy Matomo on a VPS →How to Deploy Mattermost on a VPS →How to Deploy Meilisearch on a VPS →How to Deploy Memos on a VPS →How to Deploy a Minecraft Server on a VPS →How to Deploy n8n on a VPS →How to Deploy Navidrome on a VPS →How to Deploy NetBird on a VPS →How to Deploy Netdata on a VPS →How to Deploy Nextcloud on a VPS →How to Deploy Next.js to a VPS →How to Deploy Nginx Proxy Manager on a VPS →How to Deploy NocoDB on a VPS →How to Deploy ntfy on a VPS →How to Deploy Odoo on a VPS →How to Deploy Ollama on a VPS →How to Deploy Open WebUI on a VPS →How to Deploy OpenHands on a VPS →How to Deploy OpenObserve on a VPS →How to Deploy OpenProject on a VPS →How to Deploy Outline on a VPS →How to Deploy Pangolin on a VPS →How to Deploy Paperless-ngx on a VPS →How to Deploy Passbolt on a VPS →How to Deploy Plane on a VPS →How to Deploy Plausible Analytics on a VPS →How to Deploy Plex Media Server on a VPS →How to Deploy Pocket ID on a VPS →How to Deploy PocketBase on a VPS →How to Deploy Prometheus on a VPS →How to Deploy Psono on a VPS →How to Deploy Radarr on a VPS →How to Deploy Rocket.Chat on a VPS →How to Deploy SigNoz on a VPS →How to Deploy Sonarr on a VPS →How to Deploy Stalwart on a VPS →How to Deploy Stirling-PDF on a VPS →How to Deploy Supabase on a VPS →How to Deploy Synapse on a VPS →How to Deploy Taiga on a VPS →How to Deploy TeamPass on a VPS →How to Deploy Tinyauth on a VPS →How to Deploy Traefik on a VPS →How to Deploy Trilium on a VPS →How to Deploy Twenty CRM on a VPS →How to Deploy Umami on a VPS →How to Deploy Uptime Kuma on a VPS →How to Deploy Vaultwarden on a VPS →How to Deploy Vikunja on a VPS →How to Deploy wg-easy on a VPS →How to Deploy Wiki.js on a VPS →How to Deploy WordPress on a VPS →How to Deploy Zabbix on a VPS →How to Deploy Zitadel on a VPS →How to Deploy Zulip on a VPS →Docker & Compose on Ubuntu 26.04 →Building AI Workflows with n8n →Install Open WebUI with Ollama →Adding AI-Powered Insights to Plausible Analytics →Building AI-Powered Apps with Supabase and pgvector →

We use analytics cookies (Google Analytics, PostHog) to see which guides are useful. No ad networks, no cross-site tracking. See our privacy policy.