- Add a top-level README (repo had none): overview, local quick start, posting via API keys / the skill endpoint, a docs index, and the admin split. - deployment.md: new "Django Admin (/admin/)" section covering how to log in, the is_superuser vs is_staff distinction, how to grant /admin/ access, and the raw-edit-bypasses-app-logic caution. - Refresh constraint wording to per-user multiple API keys. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
7.8 KiB
Deployment
Daybook deploys the same way on any Linux host with Docker: a cloud VM, a home
server, a Proxmox guest, whatever. It runs as a single Docker container
(Gunicorn) with all persistent state — the SQLite database and artifact files —
on a bind-mounted ./data directory outside the image.
Target host (cloud VM, home server, ...)
Docker Engine
daybook container
/app application code baked into the image
/data persistent volume: db.sqlite3, artifacts_store/
Files In deploy/
| File | Purpose |
|---|---|
Dockerfile |
Builds the Daybook image. |
docker-compose.yml |
Runs Daybook with a persistent ./data:/data bind mount. |
entrypoint.sh |
Runs migrations, collects static files, then starts Gunicorn. |
env.example |
Template for deployment environment variables. |
Prerequisites On The Target Host
- Docker Engine + the Docker Compose plugin
- Git (for the clone-and-pull workflow below)
docker --version
docker compose version
First-Time Setup
Clone the repo onto the host at a stable path:
git clone <REPO_URL> /opt/daybook
cd /opt/daybook
Create the environment file:
cp deploy/env.example deploy/.env
Generate a secret key:
python3 -c "import secrets; print('DJANGO_SECRET_KEY=' + secrets.token_urlsafe(50))"
Edit deploy/.env and set at minimum:
DJANGO_DEBUG=0
DJANGO_SECRET_KEY=<generated-secret>
DJANGO_ALLOWED_HOSTS=<vm-ip-or-dns-name>,localhost,127.0.0.1
DAYBOOK_DATA_DIR=/data
DATABASE_PATH=/data/db.sqlite3
ARTIFACTS_ROOT=/data/artifacts_store
There is no shared API token to configure — Daybook uses per-user API keys
created under Settings → API Keys once a user exists (each user can hold
several, one per agent, each individually revocable). Do not commit
deploy/.env.
Create the persistent data directory and bring the stack up:
mkdir -p deploy/data
docker compose -f deploy/docker-compose.yml up -d --build
Create your first login (superuser) so you have something to log in with and a personal API token to post through:
docker compose -f deploy/docker-compose.yml exec daybook python manage.py createsuperuser
Verify Deployment
docker compose -f deploy/docker-compose.yml ps
curl -s -o /dev/null -w "%{http_code}\n" http://127.0.0.1:8000/api/capabilities # expect 403
An unauthenticated capabilities check returns 403 (login/token required) —
that's expected; it confirms the app is up and enforcing auth. (Don't add
curl -f — it treats the 403 as an error. The container healthcheck uses
the login page, which is 200 without auth.) Open
http://<vm-ip>:8000/ in a browser and log in with the superuser you just
created, then create a personal key under Settings → API Keys and use it for a
smoke-test post:
curl -fsS -X POST http://127.0.0.1:8000/api/artifacts \
-H "Authorization: Bearer <your-personal-token>" \
-H "Content-Type: application/json" \
-d '{
"source": "deploy-smoke",
"kind": "markdown",
"title": "Deployment Smoke Test",
"content": "# Deployment Smoke Test\n\nDaybook accepted an artifact."
}'
Fast Redeploys
Once the host has a git clone, shipping a new version is two commands:
cd /opt/daybook
git pull
docker compose -f deploy/docker-compose.yml up -d --build
The entrypoint runs Django migrations on every start, so schema changes are
picked up automatically. deploy/.env and deploy/data/ are untouched by
git pull since they aren't tracked in the repo.
Operational Commands
docker compose -f deploy/docker-compose.yml logs -f daybook
docker compose -f deploy/docker-compose.yml restart daybook
docker compose -f deploy/docker-compose.yml down
docker compose -f deploy/docker-compose.yml up -d
docker compose -f deploy/docker-compose.yml exec daybook python manage.py check
docker compose -f deploy/docker-compose.yml exec daybook sh
Django Admin (/admin/)
Daybook ships Django's built-in admin at /admin/ as an operator
break-glass tool — for raw-data fixes: reset a password, inspect or delete
any user's artifacts, adjust a quota, audit or revoke API keys. It is
separate from the in-app Settings area, which is the everyday product
surface (see the in-app guide / docs/ for normal use).
Logging in: open /admin/ and sign in with an account that has
is_staff = True. The account you create with createsuperuser has it (plus
full superuser rights). Note the two different admin concepts:
is_superuser— Daybook's in-app admin (can reach Settings → Users).is_staff— can log into Django's/admin/site.
The createsuperuser account has both. A user promoted in-app via
Settings → Users ("Grant admin access") gets is_superuser only — so they
can administer Daybook through its own UI but cannot reach /admin/.
Regular users have neither. To grant someone /admin/ access, set staff
explicitly:
docker compose -f deploy/docker-compose.yml exec daybook \
python manage.py shell -c "from django.contrib.auth.models import User; u=User.objects.get(username='NAME'); u.is_staff=True; u.save()"
Caution: the admin edits database rows directly and bypasses app logic — e.g. deleting an artifact there does not clean up its files on disk the way the app's own delete does. Use it for inspection and targeted fixes, not routine management.
Backup And Restore
Back up deploy/data/ — it contains the SQLite database and all artifact
files:
tar -czf daybook-data-$(date +%Y%m%d-%H%M%S).tar.gz -C /opt/daybook/deploy data
For a consistent snapshot during heavy writes, stop the container first:
docker compose -f deploy/docker-compose.yml down
tar -czf daybook-data-$(date +%Y%m%d-%H%M%S).tar.gz -C /opt/daybook/deploy data
docker compose -f deploy/docker-compose.yml up -d
To restore: stop Daybook, replace deploy/data/ with the backup contents,
then start it again.
Reverse Proxy Notes
If putting Daybook behind Nginx, Caddy, Traefik, or a cloud load balancer:
- Proxy to
http://<vm-ip>:8000. - Add the public hostname to
DJANGO_ALLOWED_HOSTS. - Terminate TLS at the proxy; keep
DJANGO_DEBUG=0. - Set
DJANGO_CSRF_TRUSTED_ORIGINSto the public HTTPS origin, and configure the proxy to forwardX-Forwarded-Proto: https. Without both, login and any other POST fails with "CSRF verification failed" — Django has no way to know the original request was HTTPS once the proxy terminates TLS and forwards plain HTTP to the container.
DJANGO_ALLOWED_HOSTS=daybook.example.com,localhost,127.0.0.1
DJANGO_CSRF_TRUSTED_ORIGINS=https://daybook.example.com
Forwarding the proto header, by proxy:
Nginx: proxy_set_header X-Forwarded-Proto $scheme;
Apache: RequestHeader set X-Forwarded-Proto "https" (requires a2enmod headers)
Caddy: header_up X-Forwarded-Proto https
Deployment Checklist
deploy/.envexists with a real (non-placeholder)DJANGO_SECRET_KEY.DJANGO_DEBUG=0.DJANGO_ALLOWED_HOSTSincludes the hostname/IP being used.- If behind a reverse proxy:
DJANGO_CSRF_TRUSTED_ORIGINSis set and the proxy forwardsX-Forwarded-Proto: https(login will 403 otherwise). deploy/data/exists and persists acrossgit pull+ rebuild.docker compose -f deploy/docker-compose.yml psshowsdaybookhealthy.GET /api/capabilitiesreturns 403 without auth, 200 with a valid token or session (check the status code — don't usecurl -f, which errors on 403).- At least one superuser exists (
manage.py createsuperuser) with a key from Settings → API Keys.
Known MVP Constraints
- SQLite is used by default — fine for a single-VM self-hosted deployment.
- Per-user API keys (multiple per user, individually revocable); no shared/global key.
- Reader/browser access requires login; feeds are private per user.
- Manual drag/drop upload and content revisions are deferred.