I asked for an open-source Notion and ended up running four notes apps

The honest history of looking for a self-hosted Notion: AFFiNE, AppFlowy, SiYuan and Memos all got deployed, and the structured stuff went somewhere else entirely.

I asked for an open-source Notion and ended up running four notes apps

My dashboard has a section called "Notes & Productivity". It holds five links: AFFiNE, AppFlowy, SiYuan, Memos and NocoBase. One person, one VPS, five places to write things down. Nobody planned it like that, least of all me, and the way it happened is a decent case study in how a homelab grows when every question gets answered with a compose file.

This is the overview post. The gory details of individual apps get their own write-ups: AppFlowy's ten containers and the MinIO image that no longer existed, the AppFlowy admin account that couldn't open AppFlowy, AFFiNE's "sync storage" panel, and the two small apps that broke on day one anyway. Here I want to get down what I was looking for, what I actually tried, why each alternative won or lost, and where things stand in mid-October 2026, including the parts that don't flatter me.

The question I actually asked

The question was blunt: I already use Notion, what open-source alternative is exactly like it?

I asked it of the AI coding assistant I was building the stack with that month. The answer was a table, and to its credit the first line of it was "none of them is an exact clone". The shortlist looked like this:

ToolClosest to Notion inCatch
AFFiNEdocs, databases, an infinite whiteboardstill maturing
AppFlowypages and databases (table, board, calendar)still maturing, Flutter/Rust desktop app
Outlineteam wikino real databases
Docmostwiki with spaces and page treesyoung
Anytypeobjects, relations, setsdifferent mental model, local-first and encrypted
Logseq / Obsidianlinked notesoutliners, not Notion; Obsidian isn't open source
NocoDB / Baserowthe database half onlyAirtable, not Notion

The gaps it warned about were the ones I'd have listed myself if I'd thought about it for five minutes: Notion's API and integrations, synced blocks, and relations and rollups between databases. Those last two matter more than they sound. A lot of what I do in Notion is not "pages" at all. It's tables that point at other tables, with a formula or two on top.

It also said that every one of these can import a Notion export (markdown and CSV) in some form, so I could try one without losing my workspace. And it finished with a question: if I told it what I mostly use Notion for, it could narrow the list to one. I skipped that question. Hold on to that, because it's the most expensive thing I did all week.

Why most of the list lost before I tried it

I only deployed two of the seven rows. The rest lost on paper, and I think the reasons hold up, even if I'd phrase a couple of them less confidently now.

Outline is described everywhere as a very polished wiki, and I believe it. But a wiki is the part of Notion I lean on least, and Outline has no real database layer, so it would only have replaced the pages and left me with the tables. For a team knowledge base I'd pick it without hesitation. For one person whose "Notion" is mostly lists of things with dates, it doesn't fit.

Docmost lost for a similar reason, plus age. It's a lighter wiki with spaces and page trees, and it was described as fairly new. I already had a growing list of young projects on the box and didn't need another one whose upgrade path I'd be discovering as I went.

Anytype is the interesting one I didn't try. It's local-first and end-to-end encrypted, built around objects and relations instead of pages and databases. You can do Notion-like things with it, but the mental model is different, and its self-hosting story is about running sync infrastructure for its own clients rather than a web app on a hostname. I wanted something I could open in a browser tab from any machine. That ruled it out more than any technical flaw did, so I can't tell you how good it is.

Logseq and Obsidian are outliners and markdown note tools. They're great at linked notes and they don't feel anything like Notion. Obsidian also isn't open source, which mattered for a question that started with "open-source". And neither is a server app in the sense I was after.

NocoDB and Baserow were on the list as "the database half only", meaning Airtable clones, not Notion clones. This is the row I should have paid most attention to, because the database half was most of what I needed. I didn't add either of them, for a reason I'll get to: I already had something on the box that covered that half and did more.

That left AFFiNE and AppFlowy, the two named as covering docs, databases and boards in one app. So those were the two I asked for, in compose form, "same as the others in self-hosted", which meant behind my existing Traefik, one folder per app, secrets in a git-ignored .env.

What was already on the box

The irony is that two notes apps were already running. A week earlier I had added a batch of nine stacks behind Traefik in one go: RomM, Gogs, Memos, Dashy, SiYuan, NocoBase, BentoPDF, Firefly III and a server management panel that I kept out of git. They were written in a single pass and checked with docker compose config against dummy values. None of them had been started when they were committed. Memos and SiYuan came in that batch without much thought. Memos is a quick-capture timeline. SiYuan is a block-based personal knowledge base, closer to Logseq than to Notion. Both are one container. Both broke on first start, which gets its own post, but both have been up since.

NocoBase was in that batch too, and it sat there unconfigured for about a week. The afternoon I asked the Notion question, I had just asked a different question in another session: "I have NocoBase in self-hosted, I am lazy to configure it, what can I do with it?" Within a few minutes I had asked that session for tables for certifications with expiry flags, a homelab inventory checked against my dashboard, a table of pull requests and a customisable finance tracker. The two threads ran side by side on the same day, and I didn't notice at the time that they were answering the same question.

Trying AFFiNE

AFFiNE was the easy one. The official self-host compose lives in the AFFiNE repo under .docker/selfhost/compose.yml. The first path the assistant guessed for it returned 404, which is a small reminder that "the official compose" moves around between releases, and that it's worth finding it through the repo tree rather than an old blog post. Adapted to my conventions, it became four services: the app, a one-shot migration job, Postgres with pgvector, and Redis.

  # One-shot job: runs the DB migrations, then exits. Must finish before `app` starts.
  migration:
    image: ghcr.io/toeverything/affine:stable
    container_name: affine_migration
    restart: "no"
    networks: [internal]
    command: ["sh", "-c", "node ./scripts/self-host-predeploy.js"]

The app waits for that job with condition: service_completed_successfully. That's the right way to handle schema migrations in compose, and I'd like more projects to copy it. AppFlowy, as it turned out, didn't, and paid for it.

It came up, I registered the first account (which becomes the instance admin, so you want to be quick about it on a public hostname), and it worked. It's a good editor. The whiteboard mode is the one thing none of the other candidates have. It runs in a plain browser tab, which is how I use most things.

My first real question about it wasn't about features. It was about a settings panel that said "Sync storage", 765 KB used of 100 GB, and whether that meant my notes were going to someone's cloud after all. They weren't. AFFiNE is local-first, the 100 GB is a default quota it shows even when self-hosted, and the confusion turned into a useful conversation about what "self-hosted" does and doesn't prove when the server is a rented VPS. That has its own post.

Trying AppFlowy

AppFlowy is where the evening went, and part of the next morning.

AppFlowy the desktop app works offline with no server at all. AppFlowy Cloud, the thing you self-host for sync, collaboration and the web version, is a set of separate services: nginx in front, GoTrue for auth, the cloud backend, Postgres with pgvector, Redis, S3-compatible object storage, a worker, a search service, an admin console and the web app. Ten containers in my trimmed version, after leaving out the AI service (it wants an OpenAI key), pgadmin and the object store's web console.

I asked, slightly alarmed, why it needed all of that. The answer was a fair one. It's built as microservices, it reuses Supabase's GoTrue instead of writing its own auth, and it expects S3 for files because that's how its hosted version works. Some of it is optional (search, the admin console, the web app if you only use the native clients, the worker if you never import from Notion), and the core is six services you can't remove.

Then the problems arrived, roughly in this order. MinIO's image was gone from Docker Hub, so the first docker-compose up -d died on a pull error and we swapped in RustFS. The search service crash-looped because it started before the backend had run the database migrations. RustFS couldn't write its data folder because it runs as uid 10001 and Docker had created the folder as root. The admin account I'd defined in .env could authenticate but couldn't open the app ("User not found"), because AppFlowy deliberately gives the system admin no workspace. The admin console sent my browser an internal Docker hostname and got blocked as mixed content. Magic links returned 500 because SMTP was pointing at the placeholder smtp.example.com.

Every one of those got fixed, and the fixes are in git, one commit each: replacing MinIO, ordering the search service after the backend, giving the console the public URLs. By the next morning I had a normal account working and the console fixed. A later uptime check from my own monitoring returned 200 on the AppFlowy hostname, after an earlier 404 while all of this was in progress. It works. I just don't love what it costs to keep working, and that cost is the point of the next section.

Moving parts, counted

I'm not going to pretend I measured memory per app. I didn't, and I'm not going to make up numbers. What I can count is containers and images, straight from the compose files:

AppContainersWhat holds the dataImages to keep in step
AppFlowy Cloud10Postgres, S3 bucket (RustFS), search indexsix appflowyinc/* on :latest
AFFiNE4 (one exits after migrating)Postgres, a storage folderone app image, :stable
SiYuan1a workspace folder:latest
Memos1one SQLite file:stable

The entire Memos stack is short enough to show in full. This is the whole file, minus the timezone line:

# Runs as uid 5000. Before first start: mkdir -p data && sudo chown 5000:5000 data
services:
  memos:
    image: ghcr.io/usememos/memos:stable
    container_name: memos_app
    restart: unless-stopped
    user: "5000:5000"          # unprivileged; hence the chown above
    networks: [frontend]       # only the shared Traefik network, no database to hide
    volumes:
      - ./data:/var/opt/memos  # the SQLite file lives here: this folder IS the backup
    labels:
      - traefik.enable=true
      - traefik.http.routers.memos.rule=Host(`${MEMOS_HOST:-memos.example.com}`)
      - traefik.http.routers.memos.entrypoints=websecure
      - traefik.http.routers.memos.middlewares=default-security-headers@file,remove_identity@file
      - traefik.http.services.memos.loadbalancer.server.port=5230
      - traefik.docker.network=frontend

networks:
  frontend:
    external: true

AppFlowy's compose file is about 245 lines, with a 124-line nginx config of its own. That isn't a moral failing on AppFlowy's part. It's a real product with real-time sync, and that's what it costs. But it's the difference between an app I can reason about from memory and one I have to reread every time it misbehaves.

AppFlowy's six appflowyinc/* images on :latest are the maintenance problem in one line. My own update notes put AppFlowy, AFFiNE and NocoBase in the "pin an exact version, upgrade deliberately, back up first" row, and say in plain words that Watchtower-style auto-updating would silently move multi-image apps like AppFlowy out of sync. As of this writing those tags are still not pinned. There's a reminder in my backlog, due next week, and its title says "appflowy x6" as if the number itself were the complaint. SiYuan is on the same list, for a better reason: a minor release of it already broke my compose once, delivered quietly through :latest.

The morning after the AppFlowy saga I also said, about the next round of apps I was adding, that every app should need minimal config and maintenance effort. That turned into a working rule for everything added afterwards: one container and SQLite where possible, secrets generated by a setup script, no SMTP. AppFlowy breaks every clause of that rule. AFFiNE breaks about half. Memos and SiYuan obey it. If I'd had that rule written down a day earlier, I'm not sure AppFlowy would have made it onto the box at all.

Where the structured stuff went

Here's the part I only saw in hindsight. When I said "Notion", I was mostly describing databases: lists of things with dates, statuses, amounts and links to other lists. Look at what I actually asked NocoBase for in the same week: certifications with expiry dates, services with their images and backup status, money moving between accounts on fixed days, books and how far I am in each, later recipes and a backlog.

None of the four notes apps is good at that. AFFiNE and AppFlowy have database views, but they're databases inside documents. What I actually wanted was automation on top of the data: flag a certificate when it's less than three months from renewal, tell me on the last day of the month how much to move back to which account, push a notification to my phone when something is due. NocoBase does that natively. It's a no-code app builder on Postgres with real relations, SQL views and a workflow engine with schedules. It isn't a Notion clone at all, and that turned out to be fine, because the half of Notion I lean on hardest is the half that's really a database with forms and a scheduler.

That's also why NocoDB and Baserow never got a compose file. They'd have given me the tables. NocoBase gave me the tables plus the reminders, and it was already installed.

It ate Firefly III, too. The same commit that added AFFiNE and AppFlowy removed Firefly, and the next morning I said why: Firefly seemed like overkill for me, and the NocoBase finance tracker was fine. I'm leaving the details of how NocoBase is built and automated to other posts, because there's a lot of it. One number gives the scale: by the middle of the week, the commit that wired my phone ChatOps bot to NocoBase counted 125 tables exposed as chat commands. Recipes, calendar events, recurring tasks and the finance tables all went there over the following days, not into a notes app. The short version for this post: structured data has one home now, and it isn't any of the four.

Day to day, honestly

The daily reality is lopsided.

Memos is the only notes app with a real job. It's wired into the ChatOps bot, so typing memo call the bank tomorrow on my phone drops a private memo into it through the Memos API. That's exactly what Memos is good for: capture, not structure. The command needs an access token from Memos' settings in the bot's env file. I can't point to a log of it working end to end on the real server yet, so for now I'd call it wired, not proven.

NocoBase is where I actually live. The Home page, the reminders, the 08:00 brief, the finance tables. Most of what I'd have opened Notion for, I now open there or query from my phone.

AFFiNE and AppFlowy are both running, both reachable, and both nearly empty. The AFFiNE storage panel said 765 KB the evening I looked, and that was mostly the demo workspace it creates for new accounts. AppFlowy has a workspace for the normal account I created, and not much in it. I haven't run a Notion export into either of them, which says plenty about how urgent the migration really was.

SiYuan is up and mostly ignored. It's also the noisiest entry in my monitoring, for a dull reason. My NocoBase uptime check counted it as up with an HTTP 401. Uptime Kuma lists it as down. The 401 is SiYuan's access-code lock screen doing its job, and my Kuma setup notes tell me to accept 401 for Dashy, Dozzle and Paperless, which sit behind logins, but list SiYuan with no such note. My phone bot's check command now counts only a 2xx as up as well, so SiYuan will show as down there too until I point the checks at something that doesn't need the code. Two monitors disagreeing about one app is its own small lesson in what "up" means.

What it costs to keep all of it

Maintenance, as of mid-October 2026, looks like this.

Updates. Renovate is now set up on the repo and opens one pull request per image, but it can only bump pinned tags, and most of my notes apps aren't pinned. So in practice their updates happen when I run a pull, which is the opposite of deliberate. Pinning AppFlowy's six images, AFFiNE and SiYuan is the next concrete job.

Backups. This is the uncomfortable one. My off-site backup job is the one for Nextcloud, which goes to Backblaze. A backlog item lists the data folders of the apps I added later in the week (the alerting server, the uptime monitor, the bookmark manager, the RSS reader and so on) to add to that job. It doesn't mention affine/db, affine/storage, appflowy/db, appflowy/storage, siyuan/workspace or memos/data. I haven't checked the job's actual config, so I can't say they're missing for certain, but nothing I've written down says they're included. My update notes do say to take a database dump before upgrading AFFiNE or AppFlowy, which is a manual step done when I remember.

Accounts and exposure. Each notes app has its own login. I deliberately didn't put my Authelia 2FA gateway in front of them. Authelia guards the admin tools: logs, uptime, the dashboard. AppFlowy's native clients have to reach its API directly, and a login page in the middle would break them the same way an SSO proxy breaks the Nextcloud mobile app. The cost is that each app's own auth is the only lock, so the AppFlowy signup flag, AFFiNE's registration settings and SiYuan's access code all matter. I'm not yet sure the AppFlowy flag got flipped on the server, and that's on today's list.

Disk. Ten containers' worth of images for AppFlowy alone, plus two Postgres instances that hold almost nothing. I haven't measured the total and won't guess, but it's not free, and for two nearly empty apps it's hard to justify.

Open items

As of writing, these are the things I know are unfinished or unverified:

  • Choose between AFFiNE and AppFlowy, and remove the loser, Postgres and all.
  • Pin the image tags for AppFlowy (six images), AFFiNE and SiYuan.
  • Confirm APPFLOWY_DISABLE_SIGNUP=true on the server.
  • Confirm which notes-app folders the off-site backup actually includes, add the missing ones, and do one test restore.
  • Make the SiYuan monitors accept the lock screen's 401, or check something that doesn't need the code.
  • Prove the memo command end to end on the real server.
  • Decide whether I'm moving any Notion pages at all. Nothing has been imported so far.

What I'd do differently

I'd answer the question I skipped. The assistant asked what I mostly use Notion for, and I went straight to "give me the compose files". Ten containers later, the answer was "mostly databases", and that would have pointed at NocoBase, which I already had, on day one. AppFlowy would have stayed a desktop app on my laptop, which works offline with no server at all.

I'd evaluate on a throwaway machine, not the production VPS. Trying an app on the box I depend on means its data folders, its database and its exposed hostname are real from the first minute. All four of these apps now have folders my backup plan has to know about, and the backup note I wrote that week doesn't mention them.

I'd start a heavy app like AppFlowy from its untouched upstream compose, prove it works end to end, and only then trim it. Every cut I made was reasonable on its own. Together they turned a known-good stack into one where I was debugging startup order at night.

And I'd set a deadline for the comparison. Without one, "trying" two apps quietly became "running" two apps. The pick between AFFiNE and AppFlowy is still open. AFFiNE is ahead on footprint and on working in a plain browser tab. AppFlowy is ahead on native apps that feel like Notion. One of them should lose its Postgres. I haven't decided which, though the container count is a loud hint.