Will one server be enough for your team?
For most teams, yes: Docmost is one app plus a database and a cache, and the whole set runs happily on a single machine. Docmost's docs list no hardware requirements, so we will not invent numbers. What grows is the database and the folder of uploaded files, so plan memory and disk room ahead.
- A small cartridge server fits a wiki that is the only thing on the box. The m510 and m710x cartridges come with a console; the other cartridges give you power control only.
- An everyday single-processor server is what the dedicated server page suggests for most sites, apps and databases, and it has the console screen and power button if a firewall rule ever locks you out.
- A two-processor server makes sense when the wiki shares the machine with other busy jobs.
- On ARM, Docmost's release workflow builds arm64 images, so the Ampere Altra server works (built to order, 1 to 2 weeks). The eight-core X-Gene cartridge runs only Ubuntu 16.04 or 18.04, so leave it out.
A ready server is handed to you within 30 minutes of approval, with root access and an unmetered port, so nobody bills your team for the attachments it downloads. You manage the server; we keep the hardware and the network running. Still choosing between wikis? Compare it with Wiki.js or BookStack first.
Lock the doors, then write the stack
Install Docker first, using the commands in Docmost's own installation guide, and make sure the Engine line of docker version says 28 or above: since that release, a mapping tied to 127.0.0.1 can only be reached from the machine itself (moby#45610). In the block below, the first four lines switch the firewall on in a safe order: IPv6 switched on first, because ufw only gives an IPv6 twin to rules created after that; SSH kept open (allow your own port instead of OpenSSH if you moved it); the firewall enabled with --force so ufw does not stop to ask "Proceed (y|n)?"; and only then the web ports opened. The rest writes the compose file.
Why ports 80 and 443 need a ufw rule even though Docker publishes them: when the compose file gives a port no address, Docker publishes it on 0.0.0.0 and [::], meaning all of the server's IPv4 and IPv6 addresses. IPv4 traffic to it bypasses ufw. IPv6 traffic bypasses ufw only when Docker handles IPv6 itself; otherwise docker-proxy answers on IPv6, ufw treats it like any other program, and IPv6 visitors would be refused without the rule.
The compose file is Docmost's official one with four changes of ours, so you know what differs: secrets come from a .env file, port 3000 listens on 127.0.0.1 only, uploads land in a normal folder (/srv/docmost/files) that is easy to copy, and the Caddy container from Docmost's Caddy guide sits in front. Postgres keeps its data at /var/lib/postgresql, as Docmost's file has it for Postgres 18.
sudo sed -i 's/^IPV6=.*/IPV6=yes/' /etc/default/ufw
sudo ufw allow OpenSSH
sudo ufw --force enable
sudo ufw allow 80,443/tcp
sudo install -d -m 750 /srv/docmost /srv/docmost/files
sudo tee /srv/docmost/docker-compose.yml >/dev/null <<'EOF'
services:
docmost:
image: docmost/docmost:latest
env_file: .env
depends_on:
- db
- redis
ports:
- "127.0.0.1:3000:3000"
volumes:
- /srv/docmost/files:/app/data/storage
restart: unless-stopped
db:
image: postgres:18
environment:
POSTGRES_DB: docmost
POSTGRES_USER: docmost
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
volumes:
- db_data:/var/lib/postgresql
restart: unless-stopped
redis:
image: redis:8
command: ["redis-server", "--appendonly", "yes", "--maxmemory-policy", "noeviction"]
volumes:
- redis_data:/data
restart: unless-stopped
caddy:
image: caddy:2
depends_on:
- docmost
ports:
- "80:80"
- "443:443"
volumes:
- /srv/docmost/Caddyfile:/etc/caddy/Caddyfile:ro
- caddy_data:/data
- caddy_config:/config
restart: unless-stopped
volumes:
db_data:
redis_data:
caddy_data:
caddy_config:
EOF
Your secrets, and the first start
Docmost wants APP_SECRET to be 32 characters or longer and will not start with the placeholder value. DATABASE_URL is an ordinary Postgres address, user docmost, its password, host db, port 5432, database docmost, and it must carry the same password Postgres gets; the block shows it written out. We generate a hex password so it never needs escaping inside that address. Upstream keeps these values inline in the compose file; putting them in a .env that is created readable by root alone, before a single secret goes into it, is our own choice. Everything is generated on the server, so you never type a secret.
PGPASS=$(openssl rand -hex 24)
sudo install -m 600 /dev/null /srv/docmost/.env
sudo tee /srv/docmost/.env >/dev/null <<EOF
APP_URL=http://localhost:3000
APP_SECRET=$(openssl rand -hex 32)
POSTGRES_PASSWORD=$PGPASS
DATABASE_URL=postgresql://docmost:$PGPASS@db:5432/docmost
REDIS_URL=redis://redis:6379
EOF
unset PGPASS
sudo tee /srv/docmost/Caddyfile >/dev/null <<'EOF'
wiki.example.org {
reverse_proxy docmost:3000
}
EOF
sudo docker compose --file /srv/docmost/docker-compose.yml run --rm --no-deps --user root --entrypoint chown docmost -R node:node /app/data/storage
sudo docker compose --file /srv/docmost/docker-compose.yml up --detach docmost
sudo docker compose --file /srv/docmost/docker-compose.yml ps
Swap wiki.example.org for your own address. The run line gives the uploads folder to node, the user the Docmost image runs as. Postgres reads POSTGRES_PASSWORD and creates its user only the first time it starts, which is why .env has to be in place before the database ever runs. When ps shows the containers up, curl -fsS http://127.0.0.1:3000/api/health should answer; if it does not yet, give it a moment and try again.
Make yourself the owner, privately
Docmost's setup page stays available until a workspace exists, and the person who fills it in becomes the owner. Docmost's source code shows the setup check only refuses once a workspace is there. So you claim it through a tunnel while the site is still invisible from outside:
- On your own computer, run
ssh -L 3000:127.0.0.1:3000 -N you@203.0.113.10with your own user and server address. On the very first connection SSH prints the server's key fingerprint and asks whether to keep connecting, accepting yes, no or the fingerprint itself; answer yes once the fingerprint matches. - Browse to
http://localhost:3000. Fill in your name, your email and a password between 8 and 70 characters long; the workspace name field is optional. - Save. You are the workspace owner, and the setup page is closed for good. Press Ctrl+C to end the tunnel.
Put it on your own address
Do these in order, so the certificate is requested only when your name already points at the server:
- Create the DNS record: an A record for the server's IPv4, plus an AAAA record if you use its free IPv6. Check with
dig +short wiki.example.orguntil the answer is your server. - Start Caddy:
sudo docker compose --file /srv/docmost/docker-compose.yml up --detach caddy. Docmost's Caddy guide explains that Caddy fetches and renews the Let's Encrypt certificate itself and passes WebSocket connections through. - Tell Docmost its address: change
APP_URLin/srv/docmost/.envtohttps://wiki.example.organd runsudo docker compose --file /srv/docmost/docker-compose.yml up --detach --force-recreate docmost.
Live co-editing runs over WebSockets. If you ever swap Caddy for another proxy, it must relay both the Upgrade header and the Connection header to Docmost, or Docmost's docs warn that pages open in read-only mode.
Invite emails and where uploads are kept
Docmost needs a way to send mail before you can invite anyone. Open /srv/docmost/.env with sudo nano, add these lines with your mail provider's details, and recreate the docmost container as in the last step above:
MAIL_DRIVER=smtp(Docmost also speaks Postmark, withPOSTMARK_TOKEN)SMTP_HOST,SMTP_PORT(587 is common),SMTP_USERNAME,SMTP_PASSWORDSMTP_SECURE=trueonly for a server that wants TLS from the start, usually on port 465MAIL_FROM_ADDRESSandMAIL_FROM_NAME, the sender your colleagues see
Uploads stay on the server by default, in the files folder above. If you would rather keep them in an S3-compatible bucket, set STORAGE_DRIVER=s3 with the AWS_S3_* access key, secret, region, bucket and, for stores outside AWS, endpoint and path-style settings; that bucket then needs a backup of its own. Each upload is capped by FILE_UPLOAD_SIZE_LIMIT, 50mb unless you change it.
Nightly backups, a real restore and safe updates
Docmost's docs do not cover backups, so this routine is our own. Each night it saves the database, every uploaded file and the settings you need to bring them back. We skip Redis on purpose: going by the server's dependency list it holds job queues, caches and live-editing sync, which, as we read it, is not where pages or files live. Copy the backup folder to another machine yourself, for instance with an rsync pull.
# file: /usr/local/sbin/docmost-nightly.sh
set -eu
umask 077
OUT=/srv/backups/docmost
DAY=$(date +%F)
mkdir -p -m 700 "$OUT"
docker compose --file /srv/docmost/docker-compose.yml exec --no-TTY db pg_dump --username=docmost --format=custom docmost > "$OUT/db-$DAY.dump"
tar -C /srv/docmost -czf "$OUT/docmost-$DAY.tgz" files .env docker-compose.yml Caddyfile
find "$OUT" -type f -mtime +14 -delete
# file: /etc/cron.d/docmost-nightly
15 2 * * * root /bin/bash /usr/local/sbin/docmost-nightly.sh >> /var/log/docmost-nightly.log 2>&1
Bringing a backup back on the same server (DAY is the date in the file names):
- Confirm both files are there:
sudo test -s /srv/backups/docmost/db-DAY.dump && sudo test -s /srv/backups/docmost/docmost-DAY.tgz && echo found. Stop here unless you see found. - Pause the wiki:
sudo docker compose --file /srv/docmost/docker-compose.yml stop docmost caddy. - Keep the current database as a spare:
sudo docker compose --file /srv/docmost/docker-compose.yml exec --no-TTY db psql --username=docmost --dbname=postgres --command='ALTER DATABASE docmost RENAME TO spare_docmost', then make an empty one withsudo docker compose --file /srv/docmost/docker-compose.yml exec --no-TTY db createdb --username=docmost docmost. - Load the backup:
sudo sh -c 'docker compose --file /srv/docmost/docker-compose.yml exec --no-TTY --user postgres db pg_restore --dbname=docmost --username=docmost < /srv/backups/docmost/db-DAY.dump'. - Swap the uploads:
sudo mv /srv/docmost/files /srv/docmost/files.spare, thensudo tar -C /srv/docmost -xzf /srv/backups/docmost/docmost-DAY.tgz files, thensudo chown -R --reference=/srv/docmost/files.spare /srv/docmost/files. - Start it again with
sudo docker compose --file /srv/docmost/docker-compose.yml up --detach docmost caddy, check the health address, and open a page that has an attachment. - Happy with it? Remove the spares:
sudo rm -rf /srv/docmost/files.spareandsudo docker compose --file /srv/docmost/docker-compose.yml exec --no-TTY db dropdb --username=docmost spare_docmost.
Moving to a new server: set up the firewall and Docker, run sudo install -d -m 750 /srv/docmost, unpack the whole archive there so .env exists before Postgres first starts, start the database and cache with sudo docker compose --file /srv/docmost/docker-compose.yml up --detach db redis, keep running sudo docker compose --file /srv/docmost/docker-compose.yml exec --no-TTY db pg_isready --username=docmost until it says it is accepting connections, then follow steps 3, 4 and 6.
For updates, Docmost's guide pulls docmost/docmost:latest and recreates the container. We wrap that in a little care: look through what changed in the GitHub release notes, run sudo bash /usr/local/sbin/docmost-nightly.sh for a fresh backup, then sudo docker compose --file /srv/docmost/docker-compose.yml pull docmost and sudo docker compose --file /srv/docmost/docker-compose.yml up --detach --force-recreate docmost. Docmost's release workflow also tags every release with its version, for example docmost/docmost:0.96.0, so if an update goes wrong you can point the image at the previous version and restore the backup you took just before. We would not ask an older image to open data that a newer release has already written to.
What the licence lets you do
Docmost's README says the core is open source under AGPL 3.0, and that enterprise features come under a separate Docmost Enterprise license covering every file in apps/server/src/ee, apps/client/src/ee and packages/ee. The server folder is a git submodule pointing to a separate docmost/ee repository, and the Enterprise license text itself sits in packages/ee/LICENSE. That licence permits running the code in production only under a paid subscription that covers your seat count, while copying and changing it for development and testing is allowed without one.
In practice there are three editions: Open Source (free, AGPL-3.0), Business and Enterprise. A licence key, pasted under Settings, License & Edition, switches the paid features on; if it lapses, Docmost's docs say you fall back to the open source edition and keep every account and page. How we read AGPL-3.0, as a general reading and not legal advice: you can run and adapt the core freely; if you hand copies to anyone else you owe them the source, and if you let people use a changed version over the network you must offer them its source as well.
Questions people ask
- Does it cost anything to run Docmost yourself?
- The Open Source edition costs nothing and is licensed under AGPL-3.0; you pay only for the server it runs on. Business and Enterprise add paid features and need a licence key. Docmost offers trial keys through its customer portal.
- Can my team sign in with SSO on the free edition?
- Docmost's installation guide lists SSO as an enterprise feature, alongside audit logs, SCIM provisioning, AI and Bases. Its sign-in guide tags LDAP, SAML 2.0 and OIDC as enterprise, while plain email-and-password accounts carry no such tag. Docmost's pricing page compares the editions feature by feature.
- Why can I read pages but not edit them?
- That is the sign of blocked WebSockets: Docmost's docs say the editor becomes read-only without them. Check that your proxy relays both the Upgrade header and the Connection header. With the Caddy setup on this page that is handled for you.
- Is the free edition enough to replace a hosted wiki for a small team?
- The editions page says the Open Source edition includes Docmost's core documentation and collaboration features, and the README lists real-time editing, spaces, permissions, groups, comments, page history, search, attachments and diagrams among them. SSO and audit logs are named as paid features. Check your must-haves against the pricing comparison before you move.