Mirrored from
SETUP-DEV.mdat commitf31ec6a. 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.mdinstead. 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 enablewill honour the pinnedpackageManagerfield — currentlypnpm@11.25.0; the Dockerfile builds onnode: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
- Optional branding: in
apps/client/src/ee/licence/components/license-details.tsx, the Edition cell is a hardcoded ternary. Replace it withFreenterprise(seeREADME.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_URLmust be resolvable from inside the Gotenberg container. PDF export works by pointing Gotenberg's Chromium at{APP_URL}/pdf-render/{pageId}, solocalhostfails 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
waitForExpressionselector 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>— ifapps/server/src/eeis 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.