Cella PDF Server: the sync server for Cella PDF (IT guide)

Cella PDF's Sync menu keeps a project's drawings and everyone's markups in step across the office: people draw on the same sheet and see each other's markups arrive live, with every change kept as history. Sheet changes travel too: a sheet turned, moved, inserted, deleted or renamed on one computer changes on everyone's, and so do the custom columns, sequences, revision schedule and bookmarks, and each sheet's scales, scale regions, hyperlinks and Spaces. The server behind it is Cella PDF Server: a shared hub for the firm's drawings. (Its program, files, settings and Docker image keep the working name atrium.) This guide is for whoever sets it up.

There are two ways to have one:

Self-hostedHosted by Sorted Solution
Who runs ityour IT, on your server or cloudSorted Solution, one private instance per firm
Addressyours, e.g. https://sync.yourfirm.comset up for you by Sorted Solution
Costfree with Cella PDFa subscription: ask Sorted Solution about plans, seats and storage
Backupsyours (built-in nightly backup helps)nightly, offsite, kept 30 days
Leavingit's yoursyou get the whole data folder to run yourself

Hosted firms can skip to Inviting people. Everything else here is for self-hosting.

Install: Docker · TLS. Run: Inviting people · Backups. Maintain: Upgrades · Looking after it · Monitoring · Troubleshooting. Reference: What is stored · Firewall and ports · Sizing.

Self-host in 10 minutes with Docker

Cella PDF Server is one Docker container with its web server and everything else it needs built in: nothing to build or install besides Docker.

You need: a Linux machine (a small VM is plenty) with Docker Engine and the compose plugin 2.20 or newer, a DNS name for it (sync.yourfirm.com), and ports 80 and 443 reachable from wherever people work. Port 80 is only for the certificate.

The image is published on Docker Hub as sortedsol/cellapdf-server (Linux amd64 and arm64). On a Synology NAS you install it straight from Container Manager's Registry, with nothing to download. For a Windows PC, a Mac or Ubuntu you use the setup kit, a small download that has a step-by-step guide for each, written for someone who has never used Docker. IT on any Linux can use the one-page QUICKSTART.md in the kit:

  • Synology NAS: search cellapdf-server in Container Manager's Registry, download sortedsol/cellapdf-server and run it with two folders mapped. Everything in DSM, no download and nothing typed at a command line.
  • Windows PC (setup kit): Docker Desktop and a guided setup you double-click.
  • Mac (setup kit): Docker Desktop and the same guided setup.
  • Ubuntu (setup kit): one setup script installs Docker and starts the server.

The kit also holds these guides. To use it from a command line:

curl -fsSLO https://www.cellapdf.com/updates/cella-pdf-server-docker-1.1.2.zip
unzip cella-pdf-server-docker-1.1.2.zip && cd cella-pdf-server-docker-1.1.2
cp .env.example .env
nano .env                                  # ATRIUM_DOMAIN=sync.yourfirm.com, ATRIUM_NAME=Your Firm
docker compose pull                        # sortedsol/cellapdf-server:1.1.2
docker compose run --rm atrium init        # prints the admin token ONCE: put it in your password manager
docker compose up -d

Open https://sync.yourfirm.com/admin and sign in with the token. That's it: the container fetched a certificate, the server is running as a non-root user with its data in the atrium-data volume, and a backup runs every night at 03:00 UTC into the atrium-backups volume.

Useful afterwards:

docker compose ps                          # "healthy"
docker compose logs -f atrium
docker compose exec atrium atrium admin rotate-token     # a new admin token
docker compose exec atrium atrium version

TLS

Every request carries a person's token, so use HTTPS for anything beyond a trusted office LAN.

  • Public name: done; the container gets and renews a Let's Encrypt certificate (and adds HSTS).
  • Office-only name with no public DNS (sync.yourfirm.local): in .env set ATRIUM_CADDY_TLS=tls internal (Caddy's own certificate authority: copy its root out with docker compose cp caddy:/data/caddy/pki/authorities/local/root.crt . and make each computer trust it) or put your own certificate in the kit's certs folder and set ATRIUM_CADDY_TLS=tls /certs/fullchain.pem /certs/privkey.pem. The apps need HTTPS: there is no plain-HTTP setup for them.

Inviting people

  1. Sign in at https://<your address>/admin with the admin token.
  2. Projects ▸ Create: name it like the job (2417 Riverside Clinic).
  3. Invites ▸ Create invite for the project, with a note (Sam's laptop). Copy the code (J6GV-0WDS-ZJ3B); it is shown once, works once, and lasts 14 days.
  4. Send each person the address and their code. In Cella PDF (the Mac, Windows and Linux apps have the same Sync menu): Sync ▸ Join Server…, enter both, then Sync ▸ Connect Document to Project… on a sheet.

Each device that joins gets its own token (kept in the Mac's Keychain, Windows' DPAPI or Linux's secret service). Members ▸ Remove takes someone out of a project. A lost laptop: atrium admin devices, then atrium admin revoke-device <id>; its token stops working everywhere. Headless setups can do it all from the command line: atrium admin create-project, atrium admin invite <project> --note ….

Also on the admin page. Under Projects, each project's tools and client ids disclosure shows its Project Tools (how many, who changed them last) with Clear Project Tools, and the app install ids its members hold with Release (for a reinstalled or replaced machine whose id someone else's device should take over). Streams shows live documents per device against the cap and who is present on which document; Database the schema version, size, last backup and the atrium.db.before-vN copies an upgrade left; the Audit log lists token rotations, revoked devices, cleared Project Tools and released ids. /admin/metrics carries the stream and presence counts too.

Project Tools. Everyone in a project can share tools from their Tool Chest with the whole project (Tool Chest ▸ Manage Tool Chest… ▸ Project Tools ▸ Share with Project…); they appear under Tool Chest ▸ Project Tools on every member's computer while a document of the project is open. Cella PDF Server has no roles inside a project (only the admin token and members), so any member can change or remove them; each change records who made it. Last change wins; someone whose change was replaced at the same moment is told.

Backups

  • Built in: ATRIUM_BACKUP_DIR (on by default) takes a consistent backup every night at ATRIUM_BACKUP_HOUR (UTC) and keeps ATRIUM_BACKUP_KEEP (14) dated database snapshots; the PDFs in it are shared between them, so it costs little more than one copy. The admin page shows when it last ran.
  • By hand or from cron: docker compose exec atrium atrium backup /backups --keep 14 (safe while running).
  • Offsite: copy the backup folder off the machine every night (restic, rclone, your NAS's backup). A backup on the same disk is not a backup. A backup is the whole server (every PDF and the database): Cella PDF Server creates a new backup folder readable by its own user only; keep it that way.
  • Restore: docker compose stop atrium && docker compose run --rm atrium restore /var/lib/atrium-backups --force && docker compose start atrium (the nightly backups; add --snapshot 2026-10-12 for that day). To restore a copy kept elsewhere, use docker compose run --rm -v /path/to/backup:/backup atrium restore /backup --force in its place. On a new machine run docker compose run --rm atrium init too (the admin token isn't in backups). Apps that synced after the backup was taken re-send what the server is missing by themselves.

Upgrades

Back up first, then install the new Cella PDF Server container and start it (change the image: tag in docker-compose.yml to the new version, then docker compose pull && docker compose up -d). A release that changes the database migrates it on first start, after copying it to atrium.db.before-v<N> in the data folder; to go back, restore that copy or the backup. Clients keep working throughout.

What is stored

Everything is in one data folder, the atrium-data volume:

  • atrium.db: projects; members (name, email, when they joined, last seen, a hash of each device's token); invite codes (hashed); every markup change (the markup's own data: type, position, text, author, status, replies) and every sheet change (turned, moved, inserted, deleted, renamed) and change to a document's custom columns, sequences, revision schedule or bookmarks or to a sheet's scales, hyperlinks or Spaces, and each project's shared tool set (Project Tools: the tools' names, styles and any stamp pictures, at most 500 tools and 2 MB), each with who made it and when. Nothing is ever deleted from this history.
  • the current state of each document's markups, as the apps last sent it (on connecting and after each save): the same markup data as above, held for lookup.
  • blobs/: every version of every PDF uploaded to a project, and pages inserted into a synced document.
  • admin-token.sha256: a hash of the admin token.

No passwords, no payment details, no telemetry: Cella PDF Server never contacts anyone. Presence (who has which sheet open, their pointer) is kept in memory only. Cella PDF Server doesn't encrypt its files; keep the folder and its backups on an encrypted disk.

On each person's computer, beside a synced PDF, Cella PDF keeps a hidden .cella folder: the link to the project with the changes not yet sent (<file>.sync.json), and the pages inserted while the server couldn't be reached (<file>.sync-queue/, up to 64 MB) until they upload, so quitting Cella PDF offline loses nothing. The queue empties and the pages go once the server has them; the member token is in the system keychain, never there.

Firewall and ports

PortFromFor
443/tcp (and 443/udp for HTTP/3)wherever people workthe apps and the admin page
80/tcpthe internetLet's Encrypt's challenge only; redirects to HTTPS

Cella PDF Server makes no outgoing connections except to Let's Encrypt, to renew its certificate.

Sizing

Small. A firm of 5 to 50 people should run comfortably on 1 to 2 CPUs and 1 to 2 GB of memory (an estimate; the work is relaying small JSON messages and serving PDFs). Disk is the drawings: every uploaded version of every PDF is kept, so plan for roughly twice the size of the firm's current drawing sets plus growth, and the same again for local backups. One live session is one open connection per person per open sheet.

Looking after it

Cella PDF Server needs little: it backs itself up, it sends nothing anywhere, and it has nothing to tune. What it does need is the machine kept patched, the backups checked, and people removed when they leave.

WhenWhat
Weekly (5 minutes)the admin page's Database card: the last backup is from last night; df -h on the data disk.
When someone leaves the firmMembers ▸ Remove them from each project; revoke their devices (atrium admin devices, atrium admin revoke-device <id>).
When a computer is lost or replacedrevoke that device; on a replacement, Release its old client id on the admin page (Projects ▸ tools and client ids) if the new one should take it over.
Monthlyoperating system updates (Ubuntu: unattended-upgrades does them; reboot when /var/run/reboot-required exists, out of hours); docker compose pull && docker compose up -d for the web server's patches; check for a new Cella PDF Server release and read its notes.
Every three monthsa restore test (below); check the offsite copy is still arriving; check the admin token in the password manager still signs in.
When the disk passes 70 percentgrow it. Cella PDF Server keeps every uploaded PDF version, so the disk only grows (Sizing).

A restore test:

# atrium_atrium-backups is the compose file's backup volume ("atrium" is the compose project name)
docker run --rm -v atrium_atrium-backups:/backup:ro -v atrium-drill:/var/lib/atrium sortedsol/cellapdf-server:1.1.2 restore /backup
docker run --rm -d --name atrium-drill -p 127.0.0.1:8799:8787 -v atrium-drill:/var/lib/atrium sortedsol/cellapdf-server:1.1.2
curl -s http://127.0.0.1:8799/api/v1/info        # the server's name: the backup opens
docker stop atrium-drill && docker volume rm atrium-drill

Moving to a new machine: install Cella PDF Server on the new one (same version or newer), stop the old one, take a last backup (docker compose exec atrium atrium backup /backups, or copy the atrium-backups volume), restore it on the new machine (Backups ▸ Restore), run docker compose run --rm atrium init there for a new admin token, then point the DNS name at the new machine. The address doesn't change, so nobody has to rejoin; their apps re-send anything the old server took after the backup.

Changing the address (a new DNS name): people have to join again with the new address. Keep the old name pointing at the server until everyone has.

Hosted and self-hosted, either way: a hosted firm that leaves gets its whole data folder (it restores with atrium restore as above, with no license). A self-hosting firm that wants Sorted Solution to host it sends a backup folder through a secure link; it is restored onto the firm's hosted server. Either way the address changes, so people join once more.

Removing Cella PDF Server: take a final backup and keep it as long as the firm keeps project records; then docker compose down -v (that deletes the data volumes: be sure). Each computer keeps its PDFs with every markup saved into them.

Monitoring

  • GET /health answers { "ok": true } (no token; Docker's health check uses it via atrium health). An uptime monitor (UptimeRobot, Better Stack, healthchecks.io) on https://<your address>/health catches the server, the certificate and DNS at once.
  • GET /admin/metrics (Prometheus text) with the admin token, or with ATRIUM_METRICS_TOKEN, a token that reads metrics and nothing else: projects, documents, ops, connected clients, storage bytes, seats, last backup time and success. With Prometheus, these are the alerts worth having:
    - alert: AtriumDown
      expr: up{job="atrium"} == 0
      for: 5m
    - alert: AtriumBackupFailed
      expr: atrium_last_backup_success == 0 or time() - atrium_last_backup_timestamp_seconds > 26 * 3600
    - alert: AtriumDiskGrowing            # set to 70 percent of your data disk
      expr: atrium_blob_bytes > 350e9

Troubleshooting

  • The app says it can't reach the server: curl https://<address>/api/v1/info from that computer should print the server's name, "protocol":1 and "protocols":[1,2].
  • "Sheets can't be changed on this sync server": the server is an older Cella PDF Server that keeps only markups. Upgrade it (above); the apps pick it up on their next connect.
  • "Your sheet change wasn't sent": someone uploaded a newer version of that document; in Cella PDF, Sync ▸ Update Sheets from Project, then make the change again. Nothing of the refused change reaches the project: a refused sheet delete doesn't take that sheet's markups away for anyone else.
  • "Can't undo: that sheet was deleted by …": the step you undid (or redid) was for a sheet someone else deleted meanwhile; it is skipped and taken off your Undo history. Undo / Redo of sheet changes otherwise work while synced: each sends the inverse change, so everyone's copy follows.
  • "A sheet you chose was deleted … while the dialog was open": someone else deleted a sheet while your Delete, Move To, Insert Pages or Extract Sheets dialog was up, so nothing was changed; choose the sheets again. (Sheets someone else only moved meanwhile are found where they are now.)
  • "Not updated: “A-201”…" after Update Sheets: that sheet was deleted or renumbered in the project while the confirm was up; the other sheets were updated.
  • Lost the admin token: docker compose exec atrium atrium admin rotate-token.
  • "read-only" banner: only on Sorted Solution-hosted instances whose plan has ended; renew with Sorted Solution.
  • Uploads fail over a certain size: raise ATRIUM_MAX_UPLOAD_MB.
  • "This device has 32 live documents open on this server already": one computer has more synced documents open at once than ATRIUM_MAX_STREAMS_PER_DEVICE (32) allows; close some, or raise it.
  • Someone removed from a project still sees changes for a moment: an open document stops getting them within the keep-alive interval (ATRIUM_KEEPALIVE_SECONDS, 20 s), and it can't reconnect. A lost computer: atrium admin revoke-device ID (atrium admin devices lists them).
  • All settings: docker compose exec atrium atrium help.