Innovate | Connect | Transform
ContactService statusUSD $

GICT knowledge base

How to Self-Host Docmost with Docker on Ubuntu 24.04

Published

Self Hosted
How to Self-Host Docmost with Docker

Team documentation is easier to maintain when pages, search, and uploaded files live in one workspace. Docmost provides that workspace in a browser, with a collaborative editor and a Docker installation you can run on your own VPS.

This guide helps you install Docmost 0.96.0 with PostgreSQL and Redis on Ubuntu 24.04. It uses a private SSH tunnel rather than exposing the setup page to the internet.

Private-port prerequisite: use Docker Engine 28 or newer for the loopback-published ports in this guide. Older releases have a documented localhost-port exposure limitation. Confirm from another machine that the public IP cannot reach those private service ports; a proxy login does not protect a direct bypass route.

Before you start

Use an Ubuntu 24.04 VPS with working SSH access and a sudo-capable account. You also need an SSH client and a browser on your computer. The commands below use ubuntu as the server username; substitute your own login and replace SERVER_IP with the server address.

This is a private installation for access over SSH, not a public team portal. Each person connecting this way needs an authorized SSH route to the server. A hosted wiki is simpler if you do not want to operate the application and its database. Public HTTPS, email delivery, and enterprise features are outside this guide; password-reset email will not work until a mail provider is configured.

Docmost’s basic wiki and database search do not require an enterprise license. AI, SSO, and other licensed features are separate. Start with the ordinary page editor rather than treating every button in the interface as part of the free setup.

Install Docker and create the project

On a fresh Ubuntu server, install Docker and the Compose plugin from Ubuntu’s repositories:

$ sudo apt-get update
$ sudo apt-get install -y docker.io docker-compose-v2 curl openssl
$ sudo systemctl enable --now docker
$ sudo docker version
$ sudo docker compose version

If Docker is already installed from another source, keep that working installation rather than mixing package sources. Confirm that docker compose version works; the old standalone docker-compose command is not used here.

Create a new project directory under your account:

$ mkdir -p ~/docmost
$ cd ~/docmost
$ touch .env
$ chmod 600 .env
$ openssl rand -hex 32
$ openssl rand -hex 32

Use the first generated value for APP_SECRET and the second for POSTGRES_PASSWORD. Hexadecimal values avoid special characters that would need escaping in the database URL. Keep both values private.

Create ~/docmost/.env with these contents, replacing both placeholders with the generated values:

APP_SECRET=REPLACE_WITH_FIRST_HEX_VALUE
POSTGRES_PASSWORD=REPLACE_WITH_SECOND_HEX_VALUE

Keep the application secret stable. Do not regenerate it every time you start the containers.

Define the three services

Create ~/docmost/compose.yaml with the following contents. This follows the upstream three-service layout, with a pinned Docmost version and a loopback-only web port:

services:
  docmost:
    image: docmost/docmost:0.96.0
    depends_on:
      - db
      - redis
    environment:
      APP_URL: http://localhost:18092
      APP_SECRET: "${APP_SECRET}"
      DATABASE_URL: "postgresql://docmost:${POSTGRES_PASSWORD}@db:5432/docmost"
      REDIS_URL: redis://redis:6379
      DISABLE_TELEMETRY: "true"
    ports:
      - "127.0.0.1:3000:3000"
    restart: unless-stopped
    volumes:
      - docmost_data:/app/data/storage

  db:
    image: postgres:18
    environment:
      POSTGRES_DB: docmost
      POSTGRES_USER: docmost
      POSTGRES_PASSWORD: "${POSTGRES_PASSWORD}"
    restart: unless-stopped
    volumes:
      - db_data:/var/lib/postgresql

  redis:
    image: redis:8
    command: ["redis-server", "--appendonly", "yes", "--maxmemory-policy", "noeviction"]
    restart: unless-stopped
    volumes:
      - redis_data:/data

volumes:
  docmost_data:
  db_data:
  redis_data:

PostgreSQL holds the workspace and page records. The Docmost volume holds locally uploaded files, while Redis provides supporting application services. PostgreSQL 18 uses the /var/lib/postgresql volume mount shown here; do not substitute an older version’s data path without checking its image documentation.

Only the web service publishes a host port, and only on 127.0.0.1. PostgreSQL and Redis stay on the Compose network. APP_URL is the address the browser will use through the tunnel, not the server’s public IP.

Validate the configuration without printing the expanded secrets, then download and start the services:

$ sudo docker compose config --quiet
$ sudo docker compose pull
$ sudo docker compose up -d
$ sudo docker compose ps
$ curl -fsS http://127.0.0.1:3000/api/health

The first start initializes the database before the application is ready. If the health request fails immediately, wait briefly and retry it. An Up container alone does not prove that the application finished initialization. All three services should remain running, and the health endpoint should return a successful response before you continue.

For this example, the expected healthy response is shown below. Confirm it on your own instance, then complete the editor and attachment checks; health output alone is not an end-to-end test.

{"status":"ok","info":{"database":{"status":"up"},"redis":{"status":"up"}},"error":{},"details":{"database":{"status":"up"},"redis":{"status":"up"}}}

Open the private workspace

On your own computer, open a separate terminal and start the tunnel:

$ ssh -N -o ExitOnForwardFailure=yes -L 127.0.0.1:18092:127.0.0.1:3000 ubuntu@SERVER_IP

Leave that terminal open. In your browser, visit http://localhost:18092. The address is local to your computer; SSH carries the connection to the VPS. Do not browse to http://SERVER_IP:3000, which this configuration deliberately does not expose.

Complete the first-run form with a workspace name, your name, email address, and a strong password. This creates the workspace owner. Save the password before proceeding, since this installation has no email-based recovery configured.

When the workspace opens, select General, then New page. Give it a recognizable title, such as Operations notes, and add a sentence containing distinctive words, such as Amber lantern deployment checklist. The editor saves changes automatically; leave the page open until synchronization finishes.

Spaces organize pages within the workspace. Keep the test page private: do not enable public sharing just to make a link open in another browser.

Verify editing, search, and stored files

Open the same page in a second signed-in browser session. Add a short sentence in the first session and confirm that the second displays it without reloading. This exercises the editor’s live connection rather than only checking whether the login screen loads.

Open Search or press Ctrl+K. Leave Title only off to search inside pages, then enter the distinctive words you added. Newly edited text may not appear immediately; allow it to save and retry the search. Open the matching result and verify the text is present. Then search for an unrelated phrase to distinguish a real content match from simply reopening a recent page.

On an empty line in the editor, type /file, choose File attachment, and select a small test file. Once its name appears, hover over it and use the download icon. Compare the retrieved file with the original. Reopen the page after signing out and back in: its title, content, and uploaded file should still be available.

Finally, open the private page URL in a browser session that is not signed in. It should request authentication rather than reveal the page. A working local tunnel is only a network path; it should not bypass the workspace login.

If setup or editing does not work

On the VPS, return to the project directory and inspect the application logs:

$ cd ~/docmost
$ sudo docker compose logs --tail=80 docmost
  • The application keeps restarting: check that both .env values were replaced and that the application secret is at least 32 characters. Check database and Redis logs if the application cannot connect. Do not post expanded configuration or credentials in a public troubleshooting request.
  • The browser cannot connect: keep the SSH tunnel open and check for a local-port conflict. If you select a different local port, update APP_URL to match and apply the configuration again with sudo docker compose up -d.
  • Pages open, but edits do not synchronize: the editor needs WebSockets. This direct SSH tunnel carries them without a reverse proxy. If you later introduce a proxy, verify its WebSocket support rather than assuming an HTTP page load proves it works.
  • Uploads fail: inspect free disk space and the application logs. Keep the local-storage volume mounted at /app/data/storage; an unmounted container directory is not persistent file storage.

You now have a private Docmost workspace with a working editor, searchable pages, and stored files. Keep the terminal tunnel open while using it; closing the tunnel disconnects your browser without stopping Docmost on the VPS.

Reference: Docmost installation. Check the documentation for your installed version before applying changes.

Related guides

Account services are unavailable until the production backend is configured.