How to Deploy ONLYOFFICE Docs on a VPS
Updated Oct 2026
verified on Ubuntu 26.04 · Oct 2026Self-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.
- 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
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.)
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:
- Install the ONLYOFFICE app from the Nextcloud app store (it is a connector, not the editor itself).
- In Administration settings → ONLYOFFICE, set the Document Editing
Service address to
https://docs.example.com/. - Paste the
JWT_SECRETvalue from~/onlyoffice/.envinto the Secret key field and save. Print it withgrep JWT_SECRET ~/onlyoffice/.env. - 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.