2 Setup Dev
Ulysia edited this page 2026-10-06 16:54:28 +02:00

Mirrored from SETUP-DEV.md at commit f31ec6a. Edit the file in the repository: this page is regenerated from it, and edits made here are overwritten.

Development setup

Running Docmost Freenterprise from source, for development.

Deploying a server? Use INSTALL.md instead. Docker is the supported way to run this: it applies the client patches, gets the build order right and runs the migrations for you. This document is for working on the code.

The one structural thing to understand: this is two repositories. The Docmost fork is public upstream code; this EE bundle is private and lives inside it at apps/server/src/ee/, gitignored by the parent. Neither knows about the other — you clone them separately, and nothing you do here is ever pushed to github.com/docmost/docmost.


1. Prerequisites

  • Node 26.x, pnpm 11.x (corepack enable will honour the pinned packageManager field — currently pnpm@11.25.0; the Dockerfile builds on node:26-slim)
  • Postgres 16+ and Redis, reachable from the app
  • Docker, for the two sidecar containers
  • An SSH key on the server that can read ssh://git@git.derg.cz/ulysia/docmost-freenterprise.git

2. Clone both repositories

git clone https://github.com/docmost/docmost.git
cd docmost

# Pin to the upstream commit this fork was built against, rather than a
# moving main — see "Staying current" below.
#
# NOT the v0.95.0 tag, which is 13 commits behind and predates the Node 26 /
# pnpm 11 upgrade: checking it out gives a tree the Dockerfile no longer
# matches. DEPLOYMENT.md explains the pin.
git checkout 7bef7b1a

# The EE bundle goes inside, and the parent must ignore it.
git clone ssh://git@git.derg.cz/ulysia/docmost-freenterprise.git apps/server/src/ee

Reapply the parent-side changes

A fresh upstream clone still has Docmost's own ee submodule and no gitignore entry, so three small edits are needed. They're deliberately not committed anywhere public:

# 1. Drop upstream's private submodule (it is unreachable and unused)
git rm --cached apps/server/src/ee 2>/dev/null || true
rm -f .gitmodules

# 2. Stop the parent tracking our bundle
printf '\n# EE bundle is its own repository\napps/server/src/ee/\n' >> .gitignore
  1. Optional branding: in apps/client/src/ee/licence/components/license-details.tsx, the Edition cell is a hardcoded ternary. Replace it with Freenterprise (see README.md — the string is client-side and can't be driven from the server).

Verify the parent now ignores the bundle:

git status --short          # should NOT list apps/server/src/ee

3. Apply the client patches

Docker does this for you; nothing does it here. Some fixes — and two migrations — live as diffs against parent-owned files, and without them the tree is missing both.

apps/server/src/ee/scripts/apply-patches.sh            # apply
apps/server/src/ee/scripts/apply-patches.sh --revert   # undo

Idempotent both ways. This deliberately dirties the parent repo, so revert before moving to a new upstream commit — that is the one habit worth keeping. patches/README.md explains what each one fixes.

4. Install and build

pnpm install
pnpm run build              # nx orders the workspace packages correctly

Building via pnpm run build matters: @docmost/editor-ext and @docmost/base-formula both resolve through dist/, and the server imports them. Building the server alone against a fresh clone fails with TS2307: Cannot find module '@docmost/...'.

5. Sidecar containers

docker compose -f docker-compose.yml \
               -f apps/server/src/ee/docker-compose.ee.yml up -d
Service Needed for
gotenberg PDF export
tika PDF/DOCX import, attachment search indexing

Both are optional — features that need them fail with a clear message; the rest of the app is unaffected.

6. Environment

APP_URL=https://docs.example.com    # see the warning below
DATABASE_URL=postgres://...
REDIS_URL=redis://...
APP_SECRET=...

GOTENBERG_URL=http://gotenberg:3000   # core env var
TIKA_URL=http://tika:9998             # EE env var (ee/shared/ee-env.ts)

APP_URL must be resolvable from inside the Gotenberg container. PDF export works by pointing Gotenberg's Chromium at {APP_URL}/pdf-render/{pageId}, so localhost fails inside Docker. Use the real hostname, or a compose-network alias.

7. Migrate and start

pnpm --filter server migration:latest   # or however you run migrations
pnpm start

Nearly every table this bundle uses (user_mfa, api_keys, audit, auth_providers, scim_tokens, page_verifications, base_*) already ships in core, so most features needed no migration at all.

Two do, and both arrive as patches rather than as files in this repo, because migrations have to live in the parent's migrations/ directory to be picked up: 0005 (base row pages) and 0009 (group-driven workspace roles). Patches apply before the build, so the files compile into dist and the runner treats them like any other migration. Running from source means applying the patches first — see patches/README.md.

8. Post-start checks

In rough order of "silently wrong if broken":

-- Audit logging: log in once, then
SELECT count(*) FROM audit;     -- 0 means the no-op service won; see VERIFICATION.md
  • Licence page should read Freenterprise and list every feature.
  • MFA: enable it in account settings, log out, log back in. No external service needed.
  • Bases: create one, add a column and a row. This is the least-tested area — nothing in it has run against a database.
  • PDF export: check the produced file isn't blank. A wrong waitForExpression selector yields an empty PDF rather than an error.
  • SSO/SCIM: only after the above, and expect to iterate against Authentik's sync log.

Staying current with upstream

git fetch origin
git checkout v0.96.0            # a deliberate tag bump, not main
pnpm install && pnpm run build

Then reapply the parent-side edits from step 2 — they live outside git by design, so a tag change drops them.

The EE bundle updates independently:

git -C apps/server/src/ee pull

The bundle never edits core files (see README.md conventions), so upstream bumps shouldn't conflict with it. What can break is core changing a require() hook path or a client contract — each module documents the exact file:line it depends on, so a failure points at its own cause.

CI

  • EE image: clone both repos as above, build from source.
  • OSS image: don't build. Retag docmost/docmost:<tag> — if apps/server/src/ee is simply absent, require('./ee/ee.module') fails its try/catch and the app boots cleanly without any EE feature.

CI does neither by hand — .github/workflows/build.yml builds the EE image on every push to main. It needs no deploy key, since the workflow lives in this repo and upstream is public. See Continuous integration in DEPLOYMENT.md.