The Problem: Your Team’s Knowledge Lives Everywhere Except Where You Need It

Scenario: A new developer joins your team. They need to understand your deployment process, API conventions, and how to request production access. Where is that information?

  • Half of it is in a 14-month-old Confluence space nobody updates
  • A quarter is scattered across Slack channels with names like #general, #engineering, and #random
  • The rest lives in Google Docs that shared with someone who left the company eight months ago

Sound familiar? Most teams operate this way—information fragmented across tools, outdated the moment it’s written, and impossible to find when someone actually needs it.

This is why self-hosted knowledge bases exist. BookStack is one of the best open-source options: it’s designed around the mental model of books, chapters, and pages (which is how humans actually organize knowledge), it’s lightweight enough to run on a small VPS, and it doesn’t require a PhD in enterprise software to maintain.

In this guide, we’ll set up BookStack on your own infrastructure—giving you full control over your team’s documentation without per-user licensing fees or vendor lock-in.

Quick Answer: Is BookStack Right for Your Team?

If you need a simple, hierarchical documentation system that non-technical people can actually use: BookStack is an excellent choice. The book/chapter/page metaphor requires zero training.

If you need a wiki where anyone can edit anything: BookStack works, but consider MediaWiki if you want true wiki-style editing.

If you need enterprise features like SSO, advanced permissions, or audit logs: Consider Confluence or Notion, but be prepared to pay per-user fees.

If you’re a small-to-medium team (5-50 people) who wants control without complexity: BookStack hits the sweet spot.

What You’ll Need

For a production BookStack instance serving a small-to-medium team, we recommend:

Resource Minimum Recommended
CPU 1 vCPU 2 vCPU
RAM 2 GB 4 GB
Storage 20 GB SSD 50 GB SSD
OS Ubuntu 22.04 LTS or 24.04 LTS
Docker Docker + Docker Compose

Hosting recommendation: A Canadian Web Hosting Cloud VPS with 4 GB RAM gives you room to grow. BookStack is lightweight—most teams won’t hit resource limits unless they’re storing thousands of PDFs or heavy images. If you’re primarily documenting processes, APIs, and procedures, 2 GB is plenty.

Step 1: Prepare Your Server

Start with a fresh Ubuntu server. Update packages and install Docker:

sudo apt update && sudo apt upgrade -y
sudo apt install -y ca-certificates curl gnupg

# Add Docker's official GPG key and repository
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo $VERSION_CODENAME) stable" | sudo tee /etc/apt/sources.list.d/docker.list

sudo apt update
sudo apt install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin

# Enable Docker to start on boot
sudo systemctl enable --now docker

What this does: Installs Docker Engine and the Docker Compose plugin from the official Docker repository. We’re using the signed repository for security.

Step 2: Create the BookStack Directory Structure

Create a dedicated directory for BookStack and its data:

sudo mkdir -p /opt/bookstack/{mysql,public,storage}
sudo chown -R $USER:$USER /opt/bookstack
cd /opt/bookstack

Why separate directories: Keeping MySQL data, public uploads, and storage in separate volumes makes backups easier and lets you move data between servers if needed.

Step 3: Create the Docker Compose File

Create docker-compose.yml in /opt/bookstack:

nano docker-compose.yml

Paste this configuration:

services:
  bookstack:
    image: lscr.io/linuxserver/bookstack:latest
    container_name: bookstack
    environment:
      - PUID=1000
      - PGID=1000
      - APP_URL=https://docs.yourcompany.com
      - DB_HOST=db
      - DB_DATABASE=bookstack
      - DB_USERNAME=bookstack
      - DB_PASSWORD=CHANGE_THIS_TO_A_SECURE_PASSWORD
    volumes:
      - ./public:/config/www/public
      - ./storage:/config/www/storage
      - ./mysql:/config/databases
    ports:
      - "8080:80"
    depends_on:
      - db
    restart: unless-stopped

  db:
    image: mariadb:10.11
    container_name: bookstack_db
    environment:
      - MYSQL_ROOT_PASSWORD=CHANGE_THIS_ROOT_PASSWORD_TOO
      - MYSQL_DATABASE=bookstack
      - MYSQL_USER=bookstack
      - MYSQL_PASSWORD=CHANGE_THIS_TO_A_SECURE_PASSWORD
    volumes:
      - ./mysql:/var/lib/mysql
    restart: unless-stopped

Important: Replace the following before proceeding:

  • APP_URL=https://docs.yourcompany.com ? Your actual domain
  • CHANGE_THIS_TO_A_SECURE_PASSWORD ? A strong password for the BookStack database user (both places)
  • CHANGE_THIS_ROOT_PASSWORD_TOO ? A strong password for MySQL root (keep this safe)

What each section does:

  • PUID/PGID: Runs BookStack as a non-root user for security
  • APP_URL: The public URL users will access—used for generating correct links
  • DB_* variables: Connection details for the MariaDB database
  • volumes: Persists uploads, storage files, and database outside the container
  • ports: Exposes BookStack on port 8080 (we’ll add HTTPS in the next step)

Step 4: Start BookStack

Launch the containers:

docker compose up -d

Check that both containers are running:

docker compose ps

Expected output:

NAME           IMAGE                              STATUS    PORTS
bookstack      lscr.io/linuxserver/bookstack     Up        0.0.0.0:8080->80/tcp
bookstack_db   mariadb:10.11                     Up        3306/tcp

First login: Browse to http://your-server-ip:8080. You’ll be prompted to set an admin password. The default admin user is admin@admin.com — change this immediately after your first login.

Step 5: Add HTTPS with Nginx and Let’s Encrypt

Running documentation over HTTP is a security risk. Let’s add proper HTTPS with a free Let’s Encrypt certificate.

Install Nginx and Certbot:

sudo apt install -y nginx certbot python3-certbot-nginx

Create an Nginx configuration for BookStack:

sudo nano /etc/nginx/sites-available/bookstack

Paste this configuration (replace docs.yourcompany.com with your domain):

server {
    listen 80;
    server_name docs.yourcompany.com;

    location / {
        proxy_pass http://127.0.0.1:8080;
        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 $scheme;
        proxy_read_timeout 90s;
    }
}

Enable the site and test the configuration:

sudo ln -s /etc/nginx/sites-available/bookstack /etc/nginx/sites-enabled/
sudo nginx -t

If the test passes, reload Nginx:

sudo systemctl reload nginx

Now obtain your SSL certificate (ensure your domain DNS points to this server first):

sudo certbot --nginx -d docs.yourcompany.com

Certbot will ask for your email and whether to redirect HTTP to HTTPS. Choose to redirect—there’s no reason to serve documentation over HTTP.

What Certbot does: Automatically obtains a Let’s Encrypt SSL certificate, modifies your Nginx config to use HTTPS, and sets up automatic renewal.

Step 6: Configure BookStack Settings

Log in to BookStack and navigate to Settings > Application. Key configurations:

Application URL: Should already be set from your docker-compose.yml, but verify it matches your HTTPS URL.

File Upload Types: Add any additional file types your team needs to upload. Common additions:

pdf,doc,docx,xls,xlsx,ppt,pptx,csv,json,yaml,yml,sql

Default Page Editor: Choose based on your team’s preference:

  • WYSIWYG: Best for non-technical users writing procedures and guides
  • Markdown: Better for technical documentation and developers

Navigate to Settings > Registration and configure based on your needs:

  • Public registration off, invite-only: Best for internal team documentation
  • Public registration on with email domain restriction: Good for organizations where all staff need access

Step 7: Set Up Your Knowledge Structure

BookStack organizes content in a hierarchy: Books ? Chapters ? Pages. Plan your structure before creating content.

Example structure for a software team:

  • Book: Engineering Handbook
    • Chapter: Onboarding ? Pages: Dev setup, Code review process, Deployment pipeline
    • Chapter: Architecture ? Pages: System overview, Database schema, API conventions
    • Chapter: Runbooks ? Pages: Incident response, Common fixes, Escalation paths
  • Book: Product Documentation
    • Chapter: User Guides ? Pages: Getting started, Feature walkthroughs
    • Chapter: API Reference ? Pages: Authentication, Endpoints, Error codes

Tip: Don’t overthink the initial structure. BookStack lets you reorganize later—drag and drop to move pages between chapters, or chapters between books.

Step 8: Set Up Backups

Your team’s knowledge base is critical infrastructure. Set up automated backups.

Create a backup script:

nano /opt/bookstack/backup.sh

Paste this script:

#!/bin/bash
# BookStack Backup Script
# Run this daily via cron

DATE=$(date +%Y-%m-%d)
BACKUP_DIR="/opt/backups/bookstack"
RETENTION_DAYS=30

mkdir -p "$BACKUP_DIR"

# Backup MySQL database
docker exec bookstack_db mysqldump -u bookstack -p'CHANGE_THIS_TO_A_SECURE_PASSWORD' bookstack > "$BACKUP_DIR/db-$DATE.sql"

# Backup uploaded files and storage
tar -czf "$BACKUP_DIR/files-$DATE.tar.gz" /opt/bookstack/public /opt/bookstack/storage

# Delete backups older than retention period
find "$BACKUP_DIR" -name "*.sql" -mtime +$RETENTION_DAYS -delete
find "$BACKUP_DIR" -name "*.tar.gz" -mtime +$RETENTION_DAYS -delete

echo "Backup completed: $DATE"

Make it executable:

chmod +x /opt/bookstack/backup.sh

Add to cron for daily backups at 2 AM:

(crontab -l 2>/dev/null; echo "0 2 * * * /opt/bookstack/backup.sh >> /var/log/bookstack-backup.log 2>&1") | crontab -

Recovery test: After your first backup, test restoration on a separate server or local Docker instance. A backup you haven’t tested restoring is not a backup—it’s wishful thinking.

Step 9: Production Hardening

Firewall Configuration

Only expose the ports you need:

sudo ufw allow 22/tcp    # SSH
sudo ufw allow 80/tcp    # HTTP (for Let's Encrypt challenges)
sudo ufw allow 443/tcp   # HTTPS
sudo ufw enable

Database Security

The MariaDB container only accepts connections from the BookStack container, but add an additional layer by not exposing port 3306:

Verify port 3306 is not accessible from outside:

docker compose ps | grep 3306
# Should return nothing or show "127.0.0.1:3306" not "0.0.0.0:3306"

Regular Updates

BookStack releases updates regularly. Set a calendar reminder to update monthly:

cd /opt/bookstack
docker compose pull
docker compose up -d

This pulls the latest images and recreates containers with the new version. BookStack handles database migrations automatically.

Monitoring

At minimum, set up uptime monitoring for your docs URL. If your documentation goes down and nobody notices, your team will lose trust in it.

For deeper monitoring, consider self-hosted monitoring tools to monitor both the BookStack application and the database container health.

Troubleshooting

“Whoops, something went wrong” Error

Cause: Usually a database connection issue or permission problem.

Fix: Check the logs:

docker compose logs bookstack --tail 50

Common issues:

  • Database container not ready yet (wait 30 seconds and refresh)
  • Wrong database password in docker-compose.yml
  • Permissions on mounted volumes

Cannot Upload Files

Cause: File size limits in Nginx or PHP, or permissions on the uploads directory.

Fix: Increase Nginx upload limit by adding to your Nginx config:

sudo nano /etc/nginx/sites-available/bookstack

Add inside the server block:

client_max_body_size 50M;

Then reload Nginx:

sudo nginx -t && sudo systemctl reload nginx

Search Not Working

Cause: BookStack uses database search by default, which is usually fine. If search stops working, it’s typically a database issue.

Fix: Check database connectivity:

docker exec bookstack_db mysql -u bookstack -p -e "SELECT COUNT(*) FROM pages;"

If this fails, the database password may have changed or the database wasn’t created properly.

Page Shows Blank or White Screen

Cause: PHP error or missing dependency.

Fix: Check the BookStack container logs:

docker compose logs bookstack --tail 100 | grep -i error

Common causes: storage directory permissions, full disk, or corrupted cache. Try clearing the cache:

docker exec bookstack php artisan cache:clear

SSL Certificate Renewal Fails

Cause: DNS changed, port 80 blocked, or domain no longer pointing to server.

Fix: Test renewal manually:

sudo certbot renew --dry-run

Common issues:

  • Nginx config syntax error (run sudo nginx -t)
  • Firewall blocking port 80 (Let’s Encrypt needs to reach your server via HTTP)
  • DNS not pointing to correct IP (check with dig docs.yourcompany.com)

When to Use CWH Managed Support

If you’d rather focus on building product than managing infrastructure, Canadian Web Hosting’s Managed Support can handle:

  • Initial BookStack deployment and HTTPS configuration
  • Backup setup and monitoring
  • Regular security updates and BookStack version upgrades
  • Troubleshooting when things break at 11 PM before a big launch

You get the control of self-hosting with the peace of mind that someone is watching the infrastructure.

Conclusion

You now have a self-hosted knowledge base that:

  • Your team can actually use (book/chapter/page metaphor requires zero training)
  • You fully control (no vendor lock-in, no per-user fees)
  • Scales with your team (add users without license costs)
  • Keeps your documentation private (on your infrastructure, in Canadian data centres if you choose CWH)

Next steps:

  • Set up user accounts for your team members
  • Create your first book and start documenting your most critical processes
  • Consider exploring more self-hosted apps for your team behind the same Nginx reverse proxy
  • Once you have content, share the URL with your team and make it part of your onboarding process

A knowledge base is only valuable if people use it. Start with your most frequently asked questions—the things new team members always need explained. Document those, and your team will immediately see the value.