A blog you host yourself, small enough for a NAS. An AI agent can write and publish to it.
5.9K
A blog you host yourself, and an AI agent can write and publish to. One process, two SQLite files, no cloud account anywhere in the path. No algorithm deciding who sees your writing, no ads across it, and no company that can change the rules.
docker run -d --name quire \
-p 127.0.0.1:3000:3000 \
-e SITE_URL=https://example.com \
-v quire-data:/var/lib/quire/data \
-v quire-uploads:/var/lib/quire/uploads \
quireink/quireink:latest
Then read the log. A blog nobody owns yet prints the link that claims it, every time it starts:
docker logs quire
┌─────────────────────────────────────────────────────────────────────────┐
│ This blog has no owner yet. Open the link below to claim it. │
└─────────────────────────────────────────────────────────────────────────┘
https://example.com/setup?token=…
Open it and the rest is a browser: username, email, password, two-factor, then four short questions about the site. No docker exec and no interactive terminal, because a NAS log panel is enough on its own. The token
lives in memory, so a restart mints a new one, and /setup answers 404 the moment an account
exists. Anyone who would rather use the shell still can:
docker exec -it quire docker-entrypoint.sh bun run user create --username you --email [email protected].
Put a reverse proxy in front of it for TLS. The port is bound to 127.0.0.1 on purpose, and
the setup guide has an
nginx block you can copy.
Their container UIs mount real folders rather than named volumes, because that is what their
own backup jobs can see. Point both mounts at a folder you created and set PUID / PGID to
whoever owns it. The container adopts the folders on first boot and never runs the app as
root:
services:
quire:
image: quireink/quireink:latest
restart: unless-stopped
ports:
- "127.0.0.1:3000:3000"
environment:
SITE_URL: https://example.com
PUID: 1026 # Synology: Control Panel → User & Group
PGID: 100
volumes:
- /volume1/docker/quireink/data:/var/lib/quire/data
- /volume1/docker/quireink/uploads:/var/lib/quire/uploads
| Tag | What it means |
|---|---|
latest | The newest release. The one to install, because the newest release is the one carrying the fixes. |
2.2 | Fixes within the 2.2 line, no feature surprises. For anyone who would rather step up a major version by hand. |
2.2.19 | One exact release. Nothing moves, ever. |
linux/amd64 and linux/arm64, each built on its own native runner. The same image is on
GHCR as ghcr.io/joiha-steven/quireink, pushed by the same run with the same digest.
Almost nothing lives here. SMTP, the site's name and language, colours, fonts, the newsletter and the analytics settings are all entered in the admin and stored in the database.
| Variable | Default | What it does |
|---|---|---|
SITE_URL | — | Your public URL, no trailing slash. Feeds, share images and preview links are built from it. |
PUID / PGID | 1000 | Who owns the data. Only matters with bind mounts — see the NAS section. |
PORT | 3000 | Inside the container. |
DATA_DIR | /var/lib/quire/data | Both SQLite files. Mount it or lose your blog. |
STORAGE_LOCAL_DIR | /var/lib/quire/uploads | Images and files. Mount it too. |
CRON_SECRET | — | Unlocks /api/cron for an external scheduler; the route is closed without it. The built-in clock needs neither. |
ANALYTICS_TZ | UTC | The site's default timezone, until one is picked in Settings → Blog. |
The environment table has the rest.
A real editor over Markdown, with tables, footnotes, callouts, mathematics and video, that saves as you type and can hold a post until Tuesday. Six palettes in light and dark, four reading fonts, a book mode set in two columns like paper, and a five-ink highlighter whose strokes are grown rather than drawn, so no two on a page share a shape. Search that answers as you type, comments, a newsletter, analytics without cookies, and sign-in with a passkey beside the password and the code. An article page costs about 120 KB.
An AI agent can write and publish for you over MCP, through exactly the rules the admin follows, and you can take its access away at any moment.
Three more doors, the first two switched off until you turn them on: the blog can be followed from Mastodon and anywhere else that speaks ActivityPub, read by a program over a read-only JSON API, and downloaded as Markdown in a ZIP that another blog engine can import. Nothing here holds your writing hostage.
docker compose pull && docker compose up -d
Or docker pull quireink/quireink:latest and start the container again. The schema is applied
at boot, inside a transaction, so there is no migration command to run. Take a backup first.
There is a button in the admin that hands you the whole blog as one archive: both databases and every upload. Scheduled snapshots are on by default but stay on the same disk, so getting a copy off the machine is your job.
PolyForm Noncommercial, plus one additional permission that lets you run this published image commercially, paid hosting included. Only a modified version used commercially needs to ask first.
Content type
Image
Digest
sha256:331062973…
Size
88.6 MB
Last updated
about 18 hours ago
docker pull quireink/quireink