Start a Self-Hosted Wiki for Documentation
I got tired of scattering notes across Notion, Google Docs, and scattered markdown files. When I finally committed to a self-hosted wiki, everything changed. Instead of searching through five different platforms, I now have one unified knowledge base running on my home server—accessible anywhere, fully backed up, and completely under my control.
In this guide, I'll walk you through setting up a production-ready wiki using BookStack (my preference) or MediaWiki, complete with Docker Compose, reverse proxy integration, and security hardening. By the end, you'll have a powerful documentation system that scales with your needs.
Why Self-Host Your Wiki?
I've used Confluence, Notion, and Obsidian. Each had trade-offs. Notion is fast but locks your data behind their API. Obsidian is local-first but doesn't work well for team collaboration. Self-hosting gives me the best of both worlds: full data ownership, collaborative features, powerful search, and zero subscription fees.
BookStack is my top choice because it's lightweight, intuitive, and doesn't require extensive configuration. MediaWiki (the engine behind Wikipedia) is more powerful but steeper to learn. For a homelab? BookStack wins.
You'll need a VPS or home server with at least 2 cores and 2GB RAM. If you're shopping for a budget VPS, RackNerd's KVM plans start at $1.99/month and handle wikis comfortably.
Architecture: Docker Compose + Caddy + PostgreSQL
I'm running this stack:
- BookStack: The wiki application itself
- PostgreSQL: Database (more reliable than SQLite for production)
- Caddy: Reverse proxy with automatic HTTPS
- Docker Compose: Orchestration and easy backups
This setup handles updates gracefully, scales to thousands of pages, and integrates with Tailscale for remote access without exposing ports.
Docker Compose Setup
Create a directory for your wiki and save this as docker-compose.yml:
version: '3.8'
services:
postgres:
image: postgres:15-alpine
container_name: wiki-db
environment:
POSTGRES_DB: bookstack
POSTGRES_USER: bookstack
POSTGRES_PASSWORD: changeme_secure_password_here
volumes:
- postgres_data:/var/lib/postgresql/data
restart: unless-stopped
networks:
- wiki-network
bookstack:
image: linuxserver/bookstack:latest
container_name: bookstack
environment:
- PUID=1000
- PGID=1000
- APP_URL=https://wiki.example.com
- DB_HOST=postgres
- DB_PORT=5432
- DB_USER=bookstack
- DB_PASSWORD=changeme_secure_password_here
- DB_DATABASE=bookstack
- [email protected]
- MAIL_HOST=smtp.example.com
- MAIL_PORT=587
- [email protected]
- MAIL_PASSWORD=your-email-password
- MAIL_ENCRYPTION=tls
volumes:
- bookstack_data:/config
depends_on:
- postgres
restart: unless-stopped
networks:
- wiki-network
caddy:
image: caddy:latest
container_name: wiki-caddy
ports:
- "80:80"
- "443:443"
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile:ro
- caddy_data:/data
- caddy_config:/config
restart: unless-stopped
networks:
- wiki-network
volumes:
postgres_data:
bookstack_data:
caddy_data:
caddy_config:
networks:
wiki-network:
driver: bridge
changeme_secure_password_here is a placeholder. Generate a strong password with openssl rand -base64 32 before deploying. Never commit real passwords to version control.Caddy Reverse Proxy Configuration
Create a Caddyfile in the same directory:
wiki.example.com {
encode gzip
# Security headers
header / X-Frame-Options "SAMEORIGIN"
header / X-Content-Type-Options "nosniff"
header / X-XSS-Protection "1; mode=block"
header / Referrer-Policy "strict-origin-when-cross-origin"
header / Permissions-Policy "geolocation=(), microphone=(), camera=()"
# Rate limiting for login
@login_paths {
path /login*
path /auth*
}
ratelimit @login_paths 5 10s
# Reverse proxy to BookStack
reverse_proxy bookstack:80 {
header_uri X-Forwarded-Proto https
header_uri X-Forwarded-Host {http.request.host}
header_uri X-Forwarded-For {http.request.remote}
}
# Caching for static assets
@static {
path /public/* /storage/uploads/*
file
}
header @static Cache-Control "public, max-age=31536000"
}
Replace wiki.example.com with your actual domain. If you're using a subdomain on a machine behind your firewall, pair this with Tailscale or Cloudflare Tunnel (no port forwarding needed).
Getting Started
Once your compose file and Caddyfile are in place, spin everything up:
docker-compose up -d
Check logs to confirm everything started cleanly:
docker-compose logs -f bookstack
Visit your domain. BookStack will auto-create the database schema on first boot. The default credentials are:
- Email: [email protected]
- Password: password
Change these immediately. Go to Settings → Users and update your admin account. Then delete the default user if you want.
Backups and Updates
I back up my wiki using a simple script that runs daily via cron:
#!/bin/bash
BACKUP_DIR="/backups/wiki"
DATE=$(date +%Y%m%d_%H%M%S)
mkdir -p $BACKUP_DIR
# Backup PostgreSQL
docker exec wiki-db pg_dump -U bookstack bookstack | gzip > $BACKUP_DIR/bookstack_db_$DATE.sql.gz
# Backup BookStack files (covers uploaded images, attachments)
docker run --rm -v wiki_bookstack_data:/config -v $BACKUP_DIR:/backup alpine tar czf /backup/bookstack_files_$DATE.tar.gz -C /config .
# Delete backups older than 30 days
find $BACKUP_DIR -name "*.gz" -mtime +30 -delete
echo "Wiki backup completed: $DATE"
Store these backups on a separate drive or upload them to a service like Backblaze B2 or your NAS.
For updates, BookStack releases monthly. Update with one command:
docker-compose pull && docker-compose up -d
Security Hardening
Out of the box, BookStack is reasonably secure. Here's what I add:
1. Enable LDAP or OAuth if you have it (better than password reuse)
2. Set up fail2ban to block brute-force attempts on the host:
sudo apt install fail2ban
# Create /etc/fail2ban/jail.local
[DEFAULT]
bantime = 3600
findtime = 600
maxretry = 5
[sshd]
enabled = true
[caddy-http]
enabled = true
logpath = /var/log/caddy/access.log
port = http,https
filter = caddy-http
maxretry = 10
sudo systemctl restart fail2ban
3. Run backups to an offsite location (I use rclone to sync to Backblaze B2 hourly)
4. Set strong database passwords and rotate them quarterly
5. Use Tailscale for access instead of opening ports (covered separately on CompactHost)
Content Organization Tips
Once your wiki is live, here's how I organize it:
- Shelves = Major topics (e.g., "Homelab", "Security", "Self-Hosted Apps")
- Books = Sub-topics (e.g., within Homelab: "Docker", "Networking", "Storage")
- Chapters = Detailed guides (e.g., "Docker Compose Best Practices")
- Pages = Individual procedures or reference material
I use BookStack's templates feature heavily. For any repetitive documentation (e.g., server setup checklists, deployment logs), create a template and reuse it. Saves hours.
What's Next?
Your wiki is now running, backed up, and accessible. From here, I'd recommend:
- Set up full-text search indexing (BookStack does this automatically—queries are fast even with thousands of pages)
- Enable webhooks to notify you of major changes
- Start documenting your homelab infrastructure (future you will be grateful)
- Add API tokens if you want to automate page creation from scripts or other services
If you don't have a VPS yet and you're running this on shared hardware, consider offloading the database to a managed VPS from RackNerd—their infrastructure is stable and their support is responsive.