DEV Community

John
John

Posted on

Docmost Content In and Out: What Import, Storage and Handover Really Cost Over Three Years

The server bill is the small number. Over a three year client engagement, Docmost costs you far more in billable hours than in infrastructure: the Confluence and Notion imports land at partial fidelity and need human cleanup, the Postgres database and the attachment store grow on two different curves that you must budget separately, and the eventual export or handover is a project of its own rather than a button. Budget the migration in and the migration out as two priced deliverables, treat storage growth as a monthly line item, and the three year picture stops surprising you in month 30.

TL;DR by reader profile

  • The three person studio moving one client wiki (400 Confluence pages, one space, no SSO requirement): run Community Edition on a single small server and quote the import as fixed price consulting, because the cleanup hours dwarf every other cost in year one.
  • The fifteen person agency running one Docmost per client (eight active clients, separate databases): standardise one backup and restore runbook across every instance before you add client number three, because per instance operational drift is what turns eight servers into a full time job.
  • The agency migrating a client off Notion (nested pages, databases, embedded files): plan for a content model translation, not a file copy, because Notion databases have no direct Docmost equivalent and someone has to decide what each one becomes.
  • The agency that always hands over at project end (build the wiki, transfer it, walk away): write the export path into the statement of work on day one, because a handover priced after the fact is a handover you absorb at cost.
  • The agency with regulated or public sector clients (data residency clauses in the contract): keep attachments and Postgres in a jurisdiction you can name in writing, because retrofitting storage location after the content lands means a second full migration.
  • The agency evaluating Docmost against staying on Confluence (renewal in six months, no migration budget yet): price the exit from Confluence and the potential exit from Docmost together, because a one way saving is not a saving.

The central tradeoff: Docmost removes the per seat licence bill, and replaces it with hours you have to spend on import fidelity, storage growth and the eventual handover, so the question is whether your team bills those hours or eats them.


Table of contents


What does three years of Docmost content logistics actually cost?

Split the bill into four streams and price each one separately. The infrastructure stream is predictable and small. The human streams are neither.

Cost stream When it hits What drives the number
Content in Month 1 to month 3 Page count, macro and database complexity in the Confluence or Notion source, plus the review pass someone has to do page by page
Running storage Every month for 36 months Postgres row growth from page versions and comments, plus attachment volume in local disk or S3 compatible object storage
Operations Continuous, spiking at upgrades Backup verification, restore drills, container updates, and the per instance multiplier if each client gets their own deployment
Content out Final 60 days of the engagement Export fidelity, attachment relinking, and the credential and DNS work in a handover

Two specifics change the shape of that table more than anything else. The first is edition: Community Edition is AGPL-3.0 and free of seat fees, so a ten client agency pays nothing in licence and everything in hours, while the paid self managed tier converts some of those hours into a per seat subscription with a minimum seat count. The second is instance topology. One Docmost per client means one pg_dump schedule, one restore test and one upgrade window per client, multiplied by however many clients you signed.

Agencies routinely quote the import and forget the other three streams. Over 36 months, the running storage and operations lines usually exceed the import you actually invoiced for.


How faithful is the Confluence import into Docmost?

Structure survives. Anything Confluence rendered dynamically does not.

Confluence stores pages as storage format XHTML full of macro tags in the ac: and ri: namespaces. Docmost stores pages in a ProseMirror based document model. The import is a translation between two different content models, so the failure cases are systematic rather than random. Expect these categories:

  • Body text, headings, tables and lists: these map cleanly and need no review, because both formats express them as ordinary block nodes.
  • Macros: expect losses on anything computed at render time, including page trees, include and excerpt macros, JIRA issue tables, status labels and multi column layouts. Each one becomes either static text, a stripped block or nothing.
  • Attachments and inline images: files come across, but every one carries a URL that pointed at a Confluence attachment path, so links need rewriting to Docmost attachment references before the page reads correctly.
  • Internal page links: links written against Confluence page IDs or space keys do not resolve in Docmost, and a wiki of 400 pages typically carries several hundred of them.
  • Page history and comments: treat version history as non transferable and plan to leave the Confluence instance readable for a defined period instead.
  • Permissions and users: space and page restrictions do not carry an equivalent, so access has to be rebuilt in Docmost spaces by hand.

Two import routes exist. The paid self managed tier ships a Confluence migration tool. On Community Edition you export each space, then feed the resulting HTML or Markdown files into Docmost's file import. The second route costs no licence and considerably more hours.


How faithful is the Notion import into Docmost?

Notion is the harder migration, because Notion is not only a wiki. Half of a typical client workspace is databases, and Docmost has pages and spaces, not databases.

Start from the export. A Notion workspace export as Markdown and CSV produces a zip where every page is a .md file whose filename carries a 32 character page ID appended to the title, nested pages become folders, and every database arrives as a flat .csv next to a folder of its row pages. Docmost imports Markdown and HTML files, so the raw material is compatible. The content model is where the hours go.

  • Plain pages and nested hierarchies: these translate well, since the folder structure maps onto Docmost's page tree with predictable parent and child relationships.
  • Databases, views and filters: no equivalent exists. Each one needs a decision: flatten to a static Markdown table, split into one page per row, or leave it behind in Notion.
  • Relations, rollups and formulas: these are computed properties and export as static values at best, so any client process that depended on a rollup number needs rebuilding elsewhere.
  • Toggles, callouts, synced blocks and embeds: toggles and callouts degrade to ordinary blocks or blockquotes, and synced blocks duplicate rather than stay linked.
  • Filenames and internal links: the appended page IDs pollute every page title and every internal link, so budget a scripted rename and link rewrite pass rather than manual editing.
  • Attachments: files land in per page folders with URL encoded names, and each reference has to be re-pointed at Docmost storage.

Quote a Notion migration as content redesign. Quoting it as a file conversion is how agencies lose money on week two.


Post import cleanup: the line item agencies forget to quote

The import finishes in an afternoon. The cleanup runs for weeks, and it is the part clients see.

Quote it by artefact count, not page count. Before you price the work, extract the numbers from the export itself: how many attachment references, how many internal links, how many macros or database blocks. A grep -c over the exported Markdown gives you a defensible estimate in minutes, and it stops you quoting a 400 page wiki as if all 400 pages were prose.

Cleanup task Scriptable What drives the hours
Rewriting internal links Mostly, with a mapping table from old page ID to new Docmost page slug Number of links, not number of pages, and whether the source used stable IDs or titles
Re-pointing attachments Mostly, once the files are uploaded and the new paths are known Attachment count and how many were embedded inline rather than listed
Replacing lost macros and databases No One judgement call per instance, since each needs a decision about what replaces it
Rebuilding the page tree and space split Partly How closely the source hierarchy matched how the client actually works
Rebuilding permissions and group membership No Number of users, groups and previously restricted areas
Client review and sign off No Client responsiveness, which you do not control

Two practical rules. Fix links and attachments with a script before anyone opens the wiki, because manual repair after users start editing means merge conflicts with live content. And put the review pass on the client's calendar with a deadline, since an open ended sign off is an open ended invoice you cannot send.


How fast does the Docmost Postgres database grow per space?

Slower than people expect, because attachments live outside it. Docmost keeps files on local disk or in S3 compatible storage, so the database holds text, structure and metadata only. That makes Postgres the cheap half of your storage bill and the expensive half of your backup window.

Measure rather than estimate. Two queries give you everything you need for a three year projection:

  • SELECT pg_size_pretty(pg_database_size('docmost'));: run it monthly, log the result, and you have a real growth curve for the client after 90 days instead of a guess.
  • SELECT relname, pg_size_pretty(pg_total_relation_size(relid)) FROM pg_catalog.pg_statio_user_tables ORDER BY pg_total_relation_size(relid) DESC LIMIT 10;: this tells you which table is actually growing, which is rarely the one people assume.

The drivers, in the order they usually matter:

  • Page content and its editing state: each page stores structured document content plus collaborative editing state, so an actively edited page occupies more than its rendered word count suggests.
  • Page history: every saved version is a stored row. A wiki with 40 heavily revised policy pages can hold more history than content.
  • Full text search indexes: search indexes add a meaningful fraction on top of the base tables, and they grow with content volume rather than with usage.
  • Comments, mentions and activity records: high traffic client spaces accumulate these steadily even when page count is flat.

The practical consequence: a documentation wiki that would fit in a few hundred megabytes of Markdown becomes a database you must dump, transfer and restore as a single consistent unit.


Redis, websockets and the real time collaboration layer

Docmost needs Redis as well as Postgres. It is configured through REDIS_URL in the environment file and ships as a second service in the standard Docker Compose stack. Understanding what it holds decides two cost questions: how much RAM each client instance needs, and what you can safely leave out of the backup set.

  • Redis holds transient state, not documents: caching, background job queues and coordination for the collaborative editing layer live there, while the durable page content sits in Postgres. Treat Redis as rebuildable and exclude it from your backup and restore runbook.
  • The RAM floor is per instance, not per user: one Docmost per client means one Node process, one Postgres and one Redis for each of them. Eight clients is 24 containers, and the idle baseline of that stack, not peak editing load, sets the server you have to rent for 36 months.
  • Websockets need proxy configuration: real time editing runs over a websocket connection, so any reverse proxy in front of Docmost must pass Upgrade and Connection headers and tolerate long lived connections. Get this wrong and the symptom is edits that vanish on refresh, which clients report as data loss.
  • Concurrent editors drive connections, not storage: cost scales with how many people have a page open at once, which for agency client wikis is usually a handful, not the whole seat count.
  • Version pinning matters at upgrade time: pin the Redis and Postgres image tags in your compose file rather than tracking latest, so an unattended pull cannot change the database major version underneath a client instance.

Local disk or S3 for Docmost attachments over three years?

Docmost picks this with one environment variable: STORAGE_DRIVER=local or STORAGE_DRIVER=s3, with the S3 path taking AWS_S3_BUCKET, AWS_S3_REGION and AWS_S3_ENDPOINT, which means any S3 compatible service such as MinIO, Backblaze B2 or Cloudflare R2 works, not only AWS.

Dimension Local disk volume S3 compatible object storage
Monthly cost shape Bundled into the server you already rent, until the disk fills and you resize the whole machine Metered per GB stored, plus request charges, so it grows smoothly with content
Backup workflow Attachments are in the same snapshot as everything else, one artefact to restore Two systems to keep consistent, since the bucket and the database are backed up separately
Growth ceiling Hard, set by the volume size, and hit without warning when a client uploads a video Effectively none within an agency workload
Multi client isolation One directory per instance, trivially separable One bucket or prefix per client, with per client keys if you want real separation
Exit and handover Copy a directory, no egress fee Bucket to bucket copy, and egress charges apply when the data leaves the provider

The decision usually turns on the last row. Local disk keeps the exit cheap and the backup simple, which suits a wiki you expect to hand over. Object storage keeps the growth curve predictable and the server small, which suits instances you will run for the full 36 months.

Whichever you pick, set it before the import. Switching drivers later means moving every file and rewriting every attachment reference, which is the cleanup pass you already paid for once.


What has to be in a Docmost backup for the restore to actually work?

A database dump alone restores a wiki with no files, no working logins and no working configuration. Five artefacts make a restorable set, and the last two are the ones people discover are missing at the worst moment.

  • The Postgres dump: take it in custom format with pg_dump -Fc -U docmost docmost > docmost.dump rather than plain SQL, because custom format restores in parallel and lets you list contents before restoring. A dump taken while the container runs is fine, since Postgres gives you a consistent snapshot.
  • The attachment store: the mapped storage volume if you run STORAGE_DRIVER=local, or a versioned copy of the bucket if you run S3. Files and database must come from close enough in time that no page references an attachment that does not exist yet.
  • The .env file: this holds APP_SECRET, the database URL and the storage configuration. Restore a database with a different APP_SECRET and every session token signed with the old value stops validating, so users are forced back through login and any integration using an issued token breaks.
  • The compose file with pinned image tags: the version of Docmost that produced the dump is the version that can read it. Restoring a dump into a newer image and hoping the migrations run cleanly is not a recovery plan, it is a second incident.
  • What to leave out: Redis, container logs and any build cache. Including them inflates every transfer for no recovery value.

Store the set as one dated archive per client. Three files in three places is how partial restores happen.


Exporting client content out of Docmost when the engagement ends

Docmost exports pages and whole spaces to Markdown or HTML, delivered as a zip with attachments included. That covers the contractual obligation to return the content. It does not cover everything the client thinks they are getting, so agree the definition of the deliverable before the final invoice.

  • Run a sample export first, on one real space: unpack the zip and check exactly which artefacts appear before you promise anything, because what leaves the system is the only fidelity statement worth putting in writing.
  • Expect content, not context: page bodies and structure travel well in Markdown. Comments, page history, permissions, group membership and user accounts are not part of a content export, so if the client needs those, the deliverable is a database dump instead of a zip.
  • Check attachment paths in the unpacked zip: confirm that image and file links resolve to the bundled folders rather than to URLs on your server, since links pointing at an instance you are about to decommission are a broken archive with a working preview.
  • Decide the target format early: Markdown imports cleanly into Obsidian, a Git repository or another Docmost instance. HTML suits an archive that must render without tooling. Exporting to one and converting later costs a second pass.
  • Budget by space, not by wiki: export, unpack, verify and repackage is a repeatable unit of work per space, so eight client spaces is eight cycles even though the command is the same each time.

Write the export format, the excluded artefacts and the delivery medium into the statement of work at signature. Negotiating scope during an offboarding is a losing position.


Handover: transferring a running Docmost instance to the client

Two shapes exist, and they cost very differently. Transferring the server itself means an account and billing change with no data movement. Rebuilding on the client's own infrastructure means a restore, a DNS cutover and a credential rotation, which is a half day of focused work per instance plus the scheduling overhead of getting the client's IT contact in the same window.

  • Agree where it lands before you quote: the client may want it on a VPS they rent, on an office server or NAS, or on a managed Personal Cloud Server such as Yundera. Yundera is a managed Personal Cloud Server, built on CasaOS, that runs self-hosted apps as Docker containers on a server dedicated to the user. Each target implies different work for you, so pin it down at signature rather than at offboarding.
  • Rebuild, then restore, in that order: bring up the stack with the pinned image tags first, confirm it starts clean, then load the content with pg_restore -d docmost docmost.dump and copy the attachment store. Restoring into a stack that has never booted hides two failures behind one symptom.
  • Lower DNS TTL to 300 seconds a day ahead: cutover then takes minutes instead of hours, and rollback stays available if the new host misbehaves.
  • Rotate everything on the way out: database password, APP_SECRET, S3 keys and your own admin account. Your credentials should not survive the engagement.
  • Hand over a written runbook: backup command, restore command, upgrade procedure and the pinned versions. Without it, you are the unpaid support line for the next two years.

Top comments (0)