Deploy BookStack on Docker: Set Up a Self-Hosted HomeLab Wiki

HomeLab tutorial - IT technology blog
HomeLab tutorial - IT technology blog

Quick start (5 min)

Homelab documentation falls apart fast. Static IP allocations, Docker Compose files, Proxmox firewall rules, and random Bash aliases quickly scatter across scratchpads and messy text files. BookStack fixes this mess by borrowing a physical library hierarchy: Shelves, Books, Chapters, and Pages.

With Docker Compose, you can stand up a production-ready instance in under five minutes using LinuxServer’s lightweight image and a MariaDB container. The baseline stack consumes under 250MB of RAM at idle.

First, create a dedicated project directory and switch into it:

mkdir -p ~/homelab/bookstack && cd ~/homelab/bookstack

Create your Compose file:

nano docker-compose.yml

Paste the following stack configuration:

version: "3.8"

services:
  bookstack:
    image: lscr.io/linuxserver/bookstack:latest
    container_name: bookstack
    environment:
      - PUID=1000
      - PGID=1000
      - APP_URL=http://192.168.1.50:6875
      - DB_HOST=bookstack_db
      - DB_PORT=3306
      - DB_USER=bookstack
      - DB_PASS=SecretBookStackPassword123
      - DB_DATABASE=bookstackapp
    volumes:
      - ./appdata:/config
    ports:
      - 6875:80
    restart: unless-stopped
    depends_on:
      - bookstack_db

  bookstack_db:
    image: lscr.io/linuxserver/mariadb:latest
    container_name: bookstack_db
    environment:
      - PUID=1000
      - PGID=1000
      - MYSQL_ROOT_PASSWORD=RootSecretPassword456
      - MYSQL_DATABASE=bookstackapp
      - MYSQL_USER=bookstack
      - MYSQL_PASSWORD=SecretBookStackPassword123
    volumes:
      - ./dbdata:/config
    restart: unless-stopped

Swap out http://192.168.1.50:6875 with your server’s actual LAN IP address or local DNS hostname. Do not omit the port number.

Fire up the containers in the background:

docker compose up -d

Watch the migration logs until the web server starts accepting requests:

docker compose logs -f bookstack

Once you see the Nginx confirmation lines, navigate to http://<your-server-ip>:6875 in your browser. Sign in using the default administrative account:

Change this default password immediately. Go to Edit Profile in the top-right corner before building your first shelf.

Deep dive

Spinning up self-hosted services is the easy part. Remembering how you configured them eight months later during a hardware migration is where things break down. A dedicated wiki prevents that panic.

How BookStack structures your knowledge

Flat markdown trees and unstructured wikis often descend into chaos. BookStack enforces a strict four-tier hierarchy to keep documentation tidy:

  1. Shelves: Broad operational categories like Networking, Storage Pools, or Kubernetes. A shelf groups multiple related books together.
  2. Books: Individual systems or services, such as OPNsense Firewall, TrueNAS Core, or Media Automation.
  3. Chapters: Optional sections within a book. For example, VLAN Trunking or WireGuard Road-Warrior Tunnel.
  4. Pages: The actual technical articles holding code blocks, network diagrams, configuration diffs, and attachments.

This structure imposes guardrails. It prevents orphaned pages and keeps your documentation browsable even as your homelab grows to dozens of services.

Key environment parameters explained

Three Compose variables require attention before rolling into production:

  • APP_URL: BookStack uses this exact string to construct internal links, asset paths, and redirection headers. If the protocol, IP, or port differs from what sits in your browser address bar, CSS and JS assets will fail to load, and form submissions will throw CSRF errors.
  • PUID and PGID: These variables map internal container processes to host user IDs (usually 1000:1000 for the primary user on Ubuntu or Debian). This prevents the daemon from creating root-owned files on your persistent storage volumes.
  • DB_HOST: Must match the service name defined under your database block (bookstack_db). Docker’s built-in DNS handles container-to-container routing over the default bridge network automatically.

Advanced usage

Setting up HTTPS with a reverse proxy

Plain HTTP works on a protected local subnet, but modern browsers disable secure features like clipboard copy-pasting on non-HTTPS origins. If you run a reverse proxy like Caddy or Nginx, adjust your Compose file first.

Update APP_URL to match your secure FQDN:

    environment:
      - APP_URL=https://wiki.lab.example.com

Here is an example Caddy block that handles SSL generation and forwards proxy headers correctly:

wiki.lab.example.com {
    reverse_proxy 127.0.0.1:6875 {
        header_up X-Forwarded-Proto https
    }
}

For standard Nginx reverse proxies, ensure you forward the protocol and IP headers:

location / {
    proxy_pass http://127.0.0.1:6875;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto https;
}

Automating database backups

A corrupted flash drive or failed SSD shouldn’t take your runbooks down with it. A straightforward shell script running via cron creates automated daily dumps in seconds.

Create a backup script at ~/homelab/bookstack/backup.sh:

#!/usr/bin/env bash
set -euo pipefail

BACKUP_DIR="${HOME}/homelab/bookstack/backups"
TIMESTAMP=$(date +"%Y%m%d_%H%M%S")
mkdir -p "${BACKUP_DIR}"

# Dump MariaDB database from the running container
docker exec bookstack_db /usr/bin/mariadb-dump \
  -u bookstack \
  -pSecretBookStackPassword123 \
  bookstackapp | gzip > "${BACKUP_DIR}/bookstack_db_${TIMESTAMP}.sql.gz"

# Archive uploaded assets, diagrams, and attachments
tar -czf "${BACKUP_DIR}/bookstack_files_${TIMESTAMP}.tar.gz" \
  -C "${HOME}/homelab/bookstack/appdata" www/storages

# Keep only backups from the past 14 days
find "${BACKUP_DIR}" -type f -name "bookstack_*" -mtime +14 -delete

echo "Backup completed: ${TIMESTAMP}"

Make the script executable:

chmod +x ~/homelab/bookstack/backup.sh

Open crontab to schedule the task:

crontab -e

Add a nightly entry to run every morning at 03:00 AM:

0 3 * * * /bin/bash /home/ubuntu/homelab/bookstack/backup.sh >> /home/ubuntu/homelab/bookstack/backup.log 2>&1

Practical tips

Configuring Markdown as your default editor

BookStack defaults to a WYSIWYG editor. While fine for general prose, it struggles with CLI outputs, shell sessions, and raw configuration blocks. Switching to Markdown gives you a split-pane interface with clean code rendering.

  1. Log in as an administrator.
  2. Navigate to Settings > Customization.
  3. Scroll down to Page Editor and select Markdown.
  4. Save the changes. Existing pages retain their original format, while all new pages open in the dual-pane Markdown editor.

Fixing CSRF token errors on login

A “Page Expired” or “Token Mismatch” error when submitting a login form points to an environment mismatch. Verify these two settings:

  1. Ensure APP_URL in your Compose file matches the exact URL in your address bar. If you browse via http://192.168.1.50:6875, setting APP_URL=http://localhost:6875 will trigger token rejections.
  2. When running behind HTTPS proxies, confirm that your proxy passes the X-Forwarded-Proto https header. Without it, BookStack believes the connection is unencrypted and drops the session cookie.

Drafting your first structured homelab shelf

Consistency matters from day one. Here is a battle-tested structure designed for homelab fleets:

  • Shelf: Infrastructure & Baremetal
    • Book: Proxmox VE Cluster
      • Chapter: Storage Pools → Page: ZFS Scrub Schedules & Retention Policies
      • Chapter: Network Bridges → Page: VLAN Trunking on vmbr0 (10G SFP+)
    • Book: Docker Hosts
      • Page: Daemon JSON & Default Log Rotation
      • Page: Shared Compose Templates & Subnets

Setting this up now guarantees that whenever an update breaks an internal service, the exact fix is only two clicks away.

Share: