daybook/deploy/proxmox_llm_deploy.md
peter 940a703cd7 Initial public commit
Daybook: a self-hosted, multi-user Django + DRF app where agents, scripts,
or people publish artifacts and each logged-in user browses their own
time-ordered, label-filterable feed.

This squashes prior local development history (kept privately on a separate
branch) into a single clean starting point for the public repository.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-01 21:55:02 +01:00

7.3 KiB

Proxmox Container Deployment Runbook For LLM Operators

This document is written for an LLM or automation agent that is deploying Daybook into a Linux container on a Proxmox host.

The recommended deployment target is a Proxmox LXC or VM running Debian/Ubuntu with Docker and Docker Compose. Daybook itself runs as a Docker container inside that guest.

Deployment Model

Proxmox host
  Debian/Ubuntu LXC or VM
    Docker Engine
      daybook container
        /app     application code baked into image
        /data    persistent mounted volume

Persistent runtime data lives outside the image in deploy/data on the guest:

deploy/data/
  db.sqlite3
  artifacts_store/

Do not store runtime data inside the image.

Files In This Directory

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.
proxmox_llm_deploy.md This runbook.

Prerequisites On The Proxmox Guest

The guest OS should have:

  • Docker Engine
  • Docker Compose plugin
  • Git
  • Network access to pull Python base images and Python packages

For Debian/Ubuntu guests, install basics:

sudo apt-get update
sudo apt-get install -y ca-certificates curl git

Install Docker using Docker's current official instructions for the guest OS. Then verify:

docker --version
docker compose version

Clone Or Copy The Project

Choose a stable application path on the guest:

sudo mkdir -p /opt/daybook
sudo chown "$USER:$USER" /opt/daybook

If using Git:

git clone <REPO_URL> /opt/daybook
cd /opt/daybook

If files were copied by another mechanism, ensure the project root contains:

manage.py
requirements.txt
deploy/docker-compose.yml
deploy/Dockerfile

Create The Environment File

From the project root:

cd /opt/daybook
cp deploy/env.example deploy/.env

Generate secrets:

python3 - <<'PY'
import secrets
print('DJANGO_SECRET_KEY=' + secrets.token_urlsafe(50))
print('DAYBOOK_API_TOKEN=' + secrets.token_urlsafe(32))
PY

Edit deploy/.env:

nano deploy/.env

Set at minimum:

DJANGO_DEBUG=0
DJANGO_SECRET_KEY=<generated-secret>
DJANGO_ALLOWED_HOSTS=<container-ip>,<dns-name-if-any>,localhost,127.0.0.1
DAYBOOK_API_TOKEN=<generated-token>
DAYBOOK_DATA_DIR=/data
DATABASE_PATH=/data/db.sqlite3
ARTIFACTS_ROOT=/data/artifacts_store

Important:

  • Do not commit deploy/.env.
  • Do not use the development token dev-token for a real deployment.
  • Include the actual IP or DNS name users will use in DJANGO_ALLOWED_HOSTS.

Create Persistent Data Directory

From the project root:

mkdir -p deploy/data

The compose file mounts this path into the container as /data.

Build And Start

From the project root:

docker compose -f deploy/docker-compose.yml up -d --build

Watch logs:

docker compose -f deploy/docker-compose.yml logs -f daybook

Expected startup behavior:

  • Django migrations run.
  • Static files are collected.
  • Gunicorn starts on port 8000 inside the container.
  • Host port 8000 maps to container port 8000.

Verify Deployment

Check container state:

docker compose -f deploy/docker-compose.yml ps

Check capabilities endpoint from the guest:

curl -fsS http://127.0.0.1:8000/api/capabilities

Expected JSON includes:

{
  "artifact_kinds": ["html_fragment", "html_full", "markdown", "deck", "image", "pdf", "binary"],
  "auth": "bearer_token"
}

Open the app from another machine:

http://<guest-ip>:8000/

If this fails:

  • Confirm the Proxmox guest firewall allows TCP 8000.
  • Confirm any Proxmox firewall rules allow TCP 8000.
  • Confirm DJANGO_ALLOWED_HOSTS includes the hostname/IP being used.

Publish A Smoke-Test Artifact

Replace <TOKEN> with DAYBOOK_API_TOKEN from deploy/.env.

curl -fsS -X POST http://127.0.0.1:8000/api/artifacts \
  -H "Authorization: Bearer <TOKEN>" \
  -H "Content-Type: application/json" \
  -d '{
    "persona": "deploy-smoke",
    "source_type": "agent",
    "source_name": "Deployment Smoke Agent",
    "kind": "markdown",
    "title": "Deployment Smoke Test",
    "content": "# Deployment Smoke Test\n\nDaybook accepted an artifact.",
    "tags": ["deploy", "smoke"],
    "metadata": {"run_id": "deploy-smoke-001"}
  }'

Then refresh:

http://<guest-ip>:8000/

Operational Commands

Stop:

docker compose -f deploy/docker-compose.yml down

Start:

docker compose -f deploy/docker-compose.yml up -d

Rebuild after code changes:

docker compose -f deploy/docker-compose.yml up -d --build

View logs:

docker compose -f deploy/docker-compose.yml logs -f daybook

Run Django management command:

docker compose -f deploy/docker-compose.yml exec daybook python manage.py check

Open shell:

docker compose -f deploy/docker-compose.yml exec daybook sh

Backup

Back up the guest directory:

/opt/daybook/deploy/data

It contains:

  • SQLite database
  • Artifact files

Simple backup command:

tar -czf daybook-data-$(date +%Y%m%d-%H%M%S).tar.gz -C /opt/daybook/deploy data

For consistent backups during heavy writes, stop the container first:

docker compose -f /opt/daybook/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 /opt/daybook/deploy/docker-compose.yml up -d

Restore

Stop Daybook:

docker compose -f /opt/daybook/deploy/docker-compose.yml down

Replace /opt/daybook/deploy/data with the restored data directory, then start:

docker compose -f /opt/daybook/deploy/docker-compose.yml up -d

Updating The App

If deployed from Git:

cd /opt/daybook
git pull
docker compose -f deploy/docker-compose.yml up -d --build

The container entrypoint runs migrations on startup.

Reverse Proxy Notes

If putting Daybook behind Nginx, Caddy, Traefik, or a Proxmox-level reverse proxy:

  • Proxy to http://<guest-ip>:8000.
  • Add the public hostname to DJANGO_ALLOWED_HOSTS.
  • Terminate TLS at the proxy.
  • Keep DJANGO_DEBUG=0.

Example DJANGO_ALLOWED_HOSTS:

DJANGO_ALLOWED_HOSTS=daybook.example.com,localhost,127.0.0.1

LLM Deployment Checklist

Before declaring deployment complete, verify all items:

  • deploy/.env exists and does not use placeholder secrets.
  • DJANGO_DEBUG=0.
  • DJANGO_ALLOWED_HOSTS includes the access hostname/IP.
  • deploy/data/ exists.
  • docker compose -f deploy/docker-compose.yml ps shows daybook running or healthy.
  • curl -fsS http://127.0.0.1:8000/api/capabilities succeeds on the guest.
  • Browser opens http://<guest-ip>:8000/.
  • Smoke-test artifact POST succeeds with the configured bearer token.
  • deploy/data/db.sqlite3 exists after startup.
  • deploy/data/artifacts_store/ exists after posting an artifact.

Known MVP Constraints

  • SQLite is used by default. This is acceptable for local/self-hosted MVP usage.
  • The API uses a single shared bearer token.
  • No reader authentication is implemented yet.
  • Manual drag/drop upload is deferred.
  • Content revisions are deferred.