Skip to content

How to Deploy BookStack on a VPS

Updated Sep 2026

verified on Ubuntu 26.04 · Sep 2026
We earn commissions when you shop through the links below. Full disclosure →

Self-host BookStack on a small VPS — the shelves-books-pages documentation wiki, with MariaDB, a generated app key, HTTPS through Caddy, and a backup you can actually restore.

Before you start
  • A small VPS — 1 vCPU / 1 GB RAM is comfortable
  • A fresh Ubuntu 24.04 or 26.04 server with root/sudo SSH access
  • A domain or subdomain you can point at the server
  • Docker Engine + Compose installed (see the base guide below)
Need a box for this guide? Kamatera's free tier lets you spin one up now.Start free on Kamatera → (opens in new tab)

What BookStack is

BookStack is a documentation wiki built on PHP and Laravel, released under the MIT licence. Its whole idea is a fixed, book-like hierarchy: shelves hold books, books hold chapters and pages. That structure is the reason teams pick it — people who have never used a wiki understand "open the HR book, go to the onboarding chapter" without a training session — and the reason others don't: you can't nest pages ten levels deep the way you can in a free-form tool.

It gives you a WYSIWYG editor with an optional Markdown editor, page revisions, full-text search, role-based permissions down to individual pages, and login via SAML, OIDC or LDAP if you have an identity provider.

If you want real-time co-editing in the style of Notion, look at Docmost or Outline instead — BookStack vs Outline and Docmost vs BookStack lay out the trade-offs. If a stable, predictable structure is what you want, keep reading.

Server sizing

BookStack is a PHP application sitting next to a MariaDB database, and it is modest on both counts. The catalog lists a 512 MB RAM floor, and on our install-verification run (a GCP e2-standard-2, Ubuntu 26.04) the stack idled at about 93 MB of RAM with roughly 1.2 GB of disk used by images and data.

  • 1 GB RAM / 1 vCPU — comfortable for a team wiki, with room for the reverse proxy.
  • 2 GB RAM — if the same box also runs other services.

Disk depends on uploads, not text: a text-only wiki stays small for years, but attached PDFs and screenshots grow it. 20 GB is a sensible start.

Prepare the server

This guide assumes Docker Engine and the Compose plugin are installed, along with a non-root user and a ufw firewall. If not, work through Docker & Compose on Ubuntu first.

Only SSH and the reverse proxy ports should be open; BookStack's own port stays on the loopback interface:

sudo ufw status verbose

You want OpenSSH, 80/tcp and 443/tcp allowed and nothing else.

Where to host itaffiliate disclosure
Hetzner Cloudrun it on
2 vCPU · 4 GB RAM · 80 GB SSD · $23.59/mo
Get Hetzner Cloud (opens in new tab)
Kamaterafree trial
1 vCPU · 1 GB RAM · 20 GB SSD · $4.00/mo
Start free on Kamatera → (opens in new tab)
DigitalOceanalso works on
1 vCPU · 1 GB RAM · 25 GB SSD · $6.00/mo
Deploy on DigitalOcean → (opens in new tab)

Paid link — we earn a commission if you shop through it.

Install BookStack (Docker Compose)

BookStack doesn't publish its own Docker image; its installation docs list community images instead. This guide uses the linuxserver.io one, lscr.io/linuxserver/bookstack (the same image as the catalog snippet), paired with linuxserver's MariaDB image. Create a project directory:

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

BookStack needs an application key (APP_KEY) — Laravel uses it to encrypt sessions and stored secrets. The image ships a helper that prints a fresh one. Generate it, plus two database passwords, into a .env file that Compose reads automatically. The [ -f .env ] guard keeps the step safe to re-run: changing the key or passwords on an existing install would lock you out of your own data.

if [ ! -f .env ]; then
  APP_KEY=$(docker run --rm --entrypoint /bin/bash lscr.io/linuxserver/bookstack:latest appkey | tail -n 1)
  cat > .env <<EOF
APP_URL=https://docs.example.com
APP_KEY=$APP_KEY
DB_PASS=$(openssl rand -hex 16)
DB_ROOT_PASS=$(openssl rand -hex 16)
EOF
  chmod 600 .env
fi
grep -c '^APP_KEY=base64:' .env

The last line should print 1 — a key that starts with base64:. Set APP_URL to the address people will actually use: BookStack builds every link and redirect from it, so a wrong value shows up as redirects to the wrong host.

Now the compose file. Both services use bind-mounted folders next to the file, which keeps backups a matter of copying one directory:

cat > docker-compose.yml <<'YAML'
services:
  bookstack:
    image: lscr.io/linuxserver/bookstack:latest
    container_name: bookstack
    restart: unless-stopped
    depends_on: [db]
    environment:
      - PUID=1000
      - PGID=1000
      - TZ=Etc/UTC
      - APP_URL=${APP_URL}
      - APP_KEY=${APP_KEY}
      - DB_HOST=db
      - DB_PORT=3306
      - DB_USERNAME=bookstack
      - DB_PASSWORD=${DB_PASS}
      - DB_DATABASE=bookstack
    volumes:
      - ./bookstack_app_data:/config
    ports:
      # Loopback only: Caddy is the only way in from outside.
      - "127.0.0.1:6875:80"

  db:
    image: lscr.io/linuxserver/mariadb:latest
    container_name: bookstack-db
    restart: unless-stopped
    environment:
      - PUID=1000
      - PGID=1000
      - TZ=Etc/UTC
      - MYSQL_ROOT_PASSWORD=${DB_ROOT_PASS}
      - MYSQL_DATABASE=bookstack
      - MYSQL_USER=bookstack
      - MYSQL_PASSWORD=${DB_PASS}
    volumes:
      - ./bookstack_db_data:/config
YAML

Start it and wait for BookStack to answer. The first boot runs the database migrations, so give it a minute:

docker compose up -d
for i in $(seq 1 60); do
  code=$(curl -s -o /dev/null -w '%{http_code}' http://127.0.0.1:6875/login)
  [ "$code" = "200" ] && break
  sleep 5
done
echo "BookStack answered HTTP $code"
docker compose ps

HTTP 200 on /login and both containers running means the install worked. If it never answers, jump to Troubleshooting.

HTTPS + domain

Point an A record for your wiki hostname (say docs.example.com) at the server's public IP and wait for it to resolve. Then put TLS in front of 127.0.0.1:6875 — the simplest route is Automatic HTTPS with Caddy:

docs.example.com {
    reverse_proxy 127.0.0.1:6875
}

Caddy fetches and renews the certificate on its own. If you run Caddy as a container, 127.0.0.1 is the container's own loopback — put Caddy in the same compose file and use reverse_proxy bookstack:80, dropping the host port publish.

Make sure APP_URL in .env matches the HTTPS address exactly, then apply it:

cd ~/bookstack && docker compose up -d

First login and hardening

Open https://docs.example.com. The linuxserver image seeds a default administrator: admin@admin.com / password. That is public knowledge, so the first thing you do is sign in and change both the email and the password under My Account.

Then, in Settings:

  • Registration is off by default. Leave it off and invite people, or turn it on only with a restricted email domain.
  • Roles and permissions are where BookStack earns its keep. Create roles (Editor, Viewer) rather than handing out Admin, and use book-level permissions for anything sensitive.
  • Multi-factor authentication can be enforced per role. Enforce it for Admin at minimum.
  • For a team with an identity provider, BookStack supports SAML 2.0, OIDC and LDAP through environment variables — see the BookStack docs for the exact keys before you add them to the compose file.

Email is needed for invites and password resets. Add the MAIL_* variables from the BookStack docs to the bookstack service when you're ready.

Backups

Two things make up a BookStack install: the database and the files (uploads, images, themes and the .env with your APP_KEY). Lose the key and encrypted data in the database can't be read, so back up both together.

A consistent database dump, taken as the bookstack database user with the password the container already holds (the single quotes make the variables expand inside the container, not on the host). The test -s fails the step if the dump came out empty, which is how a wrong password usually shows up:

cd ~/bookstack
docker compose exec -T db sh -c 'mariadb-dump -u "$MYSQL_USER" -p"$MYSQL_PASSWORD" bookstack' > bookstack-db-$(date +%F).sql
test -s bookstack-db-$(date +%F).sql && ls -lh bookstack-db-*.sql

Then archive the app's config directory and the .env:

cd ~/bookstack
sudo tar czf bookstack-files-$(date +%F).tar.gz bookstack_app_data .env docker-compose.yml
ls -lh bookstack-files-*.tar.gz

Copy both files off the server — object storage, another machine, anywhere that survives this VPS dying. To restore, recreate the directory with the archive, start only the database (docker compose up -d db), pipe the SQL back in with mariadb, then start everything. Do that once on a scratch box so you know it works.

Upgrades

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

The container runs any new database migrations on start. Take the backup above first, and read the BookStack release notes before a large jump — some releases note changes to themes or the permission system that are worth checking.

Troubleshooting

The page redirects to the wrong address, or loads without styling. APP_URL doesn't match the URL in your browser. Fix it in .env, then docker compose up -d.

BookStack never answers on 6875. Read docker compose logs bookstack. The usual causes are the database still initializing on first boot (wait a minute) or a password mismatch between DB_PASSWORD and MYSQL_PASSWORD — both come from DB_PASS, so check you didn't edit one by hand.

"The MAC is invalid" or sessions breaking after a move. The APP_KEY changed. Restore the original .env from your backup; the key must stay the same for the life of the install.

Uploads fail on large files. Caddy doesn't cap body size by default; the limit is PHP's. Raise upload_max_filesize and post_max_size in the PHP config under bookstack_app_data/php/, then restart the container.

Verification + next steps

You're done when https://docs.example.com loads with a valid certificate, the default admin@admin.com login no longer works, a second user can sign in with the role you gave them, and you have a database dump and a file archive stored off the box.

From there: set up email so invites work, connect your identity provider if you have one, and schedule the two backup commands with cron. Comparing options first? Wiki.js vs BookStack covers the other common choice, and the Wiki.js guide walks through that install. For host picks, see Best VPS for Self-Hosting.

Next steps

How to self-host BookStack →More self-hosted wiki & docs tools →Automatic HTTPS with Caddy →Run Claude Code with Ollama on Your Own VPS →Deploy Coolify on a VPS →How to Deploy Actual Budget on a VPS →How to Deploy AnythingLLM on a VPS →How to Deploy Appwrite on a VPS →How to Deploy Audiobookshelf on a VPS →How to Deploy Authelia on a VPS →How to Deploy authentik on a VPS →How to Deploy Baserow on a VPS →How to Deploy Beszel on a VPS →How to Deploy Bitwarden on a VPS →How to Deploy CapRover on a VPS →How to Deploy Checkmate on a VPS →How to Deploy Directus on a VPS →How to Deploy docker-mailserver on a VPS →How to Deploy Docmost on a VPS →How to Deploy Dokku on a VPS →How to Deploy Dokploy on a VPS →How to Deploy Firefly III on a VPS →How to Deploy Forgejo on a VPS →How to Deploy Gatus on a VPS →How to Deploy Ghostfolio on a VPS →How to Deploy Gitea on a VPS →How to Deploy GitLab on a VPS →How to Deploy GlitchTip on a VPS →How to Deploy Grafana on a VPS →How to Deploy Graylog on a VPS →How to Deploy Headscale on a VPS →How to Deploy Healthchecks on a VPS →How to Deploy Home Assistant on a VPS →How to Deploy Immich on a VPS →How to Deploy Jan on a VPS →How to Deploy Jellyfin on a VPS →How to Deploy Karakeep on a VPS →How to Deploy Keycloak on a VPS →How to Deploy Leantime on a VPS →How to Deploy LibreChat on a VPS →How to Deploy Linkwarden on a VPS →How to Deploy LocalAI on a VPS →How to Deploy Mailcow on a VPS →How to Deploy Mailu on a VPS →How to Deploy Matomo on a VPS →How to Deploy Mattermost on a VPS →How to Deploy Meilisearch on a VPS →How to Deploy Memos on a VPS →How to Deploy n8n on a VPS →How to Deploy Navidrome on a VPS →How to Deploy NetBird on a VPS →How to Deploy Netdata on a VPS →How to Deploy Nextcloud on a VPS →How to Deploy Next.js to a VPS →How to Deploy Nginx Proxy Manager on a VPS →How to Deploy NocoDB on a VPS →How to Deploy ntfy on a VPS →How to Deploy Ollama on a VPS →How to Deploy Open WebUI on a VPS →How to Deploy OpenHands on a VPS →How to Deploy OpenObserve on a VPS →How to Deploy OpenProject on a VPS →How to Deploy Outline on a VPS →How to Deploy Pangolin on a VPS →How to Deploy Paperless-ngx on a VPS →How to Deploy Passbolt on a VPS →How to Deploy Plane on a VPS →How to Deploy Plausible Analytics on a VPS →How to Deploy Pocket ID on a VPS →How to Deploy PocketBase on a VPS →How to Deploy Prometheus on a VPS →How to Deploy Psono on a VPS →How to Deploy Radarr on a VPS →How to Deploy Rocket.Chat on a VPS →How to Deploy SigNoz on a VPS →How to Deploy Sonarr on a VPS →How to Deploy Stalwart on a VPS →How to Deploy Stirling-PDF on a VPS →How to Deploy Supabase on a VPS →How to Deploy Synapse on a VPS →How to Deploy Taiga on a VPS →How to Deploy TeamPass on a VPS →How to Deploy Tinyauth on a VPS →How to Deploy Traefik on a VPS →How to Deploy Trilium on a VPS →How to Deploy Twenty CRM on a VPS →How to Deploy Umami on a VPS →How to Deploy Uptime Kuma on a VPS →How to Deploy Vaultwarden on a VPS →How to Deploy Vikunja on a VPS →How to Deploy wg-easy on a VPS →How to Deploy Wiki.js on a VPS →How to Deploy Zabbix on a VPS →How to Deploy Zitadel on a VPS →How to Deploy Zulip on a VPS →Docker & Compose on Ubuntu 26.04 →Building AI Workflows with n8n →Install Open WebUI with Ollama →Adding AI-Powered Insights to Plausible Analytics →Building AI-Powered Apps with Supabase and pgvector →

We use analytics cookies (Google Analytics, PostHog) to see which guides are useful. No ad networks, no cross-site tracking. See our privacy policy.