Run a BookStack wiki with rootless Podman

BookStack on Debian 13, the open-source wiki that sorts your documentation into shelves, books and pages, in rootless Podman with MariaDB under a user of its own behind Caddy, with the default admin replaced first.

Tested on BookStack 26.09.1 on Debian 13 (trixie) on a Melonslab server Updated October 2, 2026

Recommended server for this guide

VC-S Micro · 2 vCPU · 8 GB Memory · 250 GB Storage

Month to month, no lock-in 7-day money-back guarantee

€7.99/mo

Deploy now
On this page

What you will set up

BookStack is a wiki for a team's documentation: routines, server notes, how-tos, onboarding. Content is sorted like a library, into shelves, books, chapters and pages, and you write in an editor that works like a word processor, or in Markdown. It keeps every version of a page, searches all text, and has an audit log of who did what.

BookStack is created by Dan Brown, a developer in the United Kingdom, and developed with its community in the open on Codeberg, a code-hosting service run by a non-profit association in Berlin. It is open source under the MIT licence. Paid support is sold by HTTP Functions Ltd, a company registered in England. In our tests it made no update check and sent no usage data. Out of the box it uses two outside services: it fetches a profile picture for every new user from Gravatar, run by Automattic in the United States, by sending a hash of the user's email address, and its diagram editor loads in the browser from diagrams.net, run by draw.io Limited in the United Kingdom. Step 3 switches both off.

BookStack publishes no container image of its own; its documentation points to two made by the community. This guide uses the one from LinuxServer.io, a group of volunteers who maintain a large set of container images. It is the first one BookStack's documentation lists, the release we tested appeared there the next day, and it runs without root here. The other one is solidnerd/bookstack.

Here BookStack runs as a pod under a user of its own called bookstack, with MariaDB for its data, behind Caddy from the Podman guide.

Every step below was run on a fresh Melonslab VC-P Alloy (2 vCPU, 8 GB) with Debian 13:

  • BookStack 26.09.1 started with MariaDB 11.8, and the default admin@admin.com account was replaced before Caddy was set up. From outside, a login with admin@admin.com and password failed.
  • Sign-ups and public access were off by default.
  • The audit log showed each visitor's real address, over IPv4 and IPv6, and ignored a forged X-Forwarded-For header. Times showed in Swedish time.
  • A shelf, a book and pages were created, with an uploaded image and a code block. Search found a word in a page's text, and an edit made a second revision. A PDF export included the image.
  • The interface was shown in Swedish, both for one user and as the default for everyone.
  • With outside services switched off, a capture of the server's traffic showed no connections when a user was created, and the diagram editor was gone.
  • Without email set up, an invite and a password reset failed, as described in step 9.
  • An update from BookStack 26.05.5 to 26.09.1 went through podman auto-update. The wiki was restored from a backup into empty volumes, and everything came back by itself after a reboot.

The pod used about 160 MB of memory, most of it MariaDB.

Before you start

You need:

  • a server set up as in the Podman guide, with Caddy running;
  • an A record and an AAAA record for wiki.example.com pointing at your server.

The examples use wiki.example.com for the wiki and 203.0.113.10 for your server's IPv4 address, which ip -brief address show eth0 shows. Replace them throughout.

1. Create the user

As root:

useradd -m -s /bin/bash bookstack
loginctl enable-linger bookstack
machinectl shell bookstack@

Everything up to step 5 runs as bookstack.

2. Create the secrets

podman run --rm --entrypoint /bin/bash lscr.io/linuxserver/bookstack:latest appkey | tr -d '\n' | podman secret create bookstack-app-key -
openssl rand -hex 24 | tr -d '\n' | podman secret create bookstack-db-password -

The first line downloads the BookStack image and uses its appkey command to make the application key, which BookStack uses to encrypt sessions and the two-factor secrets of your users. Without it a restored backup is of little use, so step 11 saves a copy. The second line makes the database password. Both are stored by Podman, not in the files below.

3. Define the pod

mkdir -p ~/.config/containers/systemd
cd ~/.config/containers/systemd

Create bookstack.pod:

[Pod]
PodName=bookstack
# Only Caddy, on this server, can reach BookStack: the port is not open to the internet.
PublishPort=127.0.0.1:8104:80

[Install]
WantedBy=default.target

Create bookstack-db.container:

[Container]
ContainerName=bookstack-db
Image=docker.io/library/mariadb:11.8
Pod=bookstack.pod
Volume=bookstack-db:/var/lib/mysql
Environment=MARIADB_DATABASE=bookstack MARIADB_USER=bookstack MARIADB_RANDOM_ROOT_PASSWORD=1 MARIADB_AUTO_UPGRADE=1
Secret=bookstack-db-password,type=env,target=MARIADB_PASSWORD
HealthCmd=healthcheck.sh --connect --innodb_initialized
HealthInterval=10s
Notify=healthy
AutoUpdate=registry

[Service]
Restart=always

And bookstack-app.container:

[Unit]
Requires=bookstack-db.service
After=bookstack-db.service

[Container]
ContainerName=bookstack-app
Image=lscr.io/linuxserver/bookstack:latest
Pod=bookstack.pod
Volume=bookstack-config:/config
Environment=APP_URL=https://wiki.example.com APP_PROXIES=203.0.113.10
Environment=TZ=Europe/Stockholm APP_DISPLAY_TIMEZONE=Europe/Stockholm
Environment=DISABLE_EXTERNAL_SERVICES=true
Environment=DB_HOST=127.0.0.1 DB_PORT=3306 DB_DATABASE=bookstack DB_USERNAME=bookstack
Secret=bookstack-app-key,type=env,target=APP_KEY
Secret=bookstack-db-password,type=env,target=DB_PASSWORD
AutoUpdate=registry

[Service]
Restart=always

What the settings do:

  • APP_URL is the address BookStack puts in every link and form. It must be the public https:// address.
  • APP_PROXIES is the address BookStack trusts to tell it the visitor's real address. Caddy's connections arrive in the pod from the server's own IPv4 address, as the Podman guide explains, so that is the one to name. Without it, the audit log shows 203.0.113.10 for everyone.
  • APP_DISPLAY_TIMEZONE shows times in Swedish time. BookStack still stores them in UTC, which is its default and keeps them right across daylight saving changes. TZ sets the time zone of the container's own logs.
  • DISABLE_EXTERNAL_SERVICES=true stops the Gravatar lookups and removes the draw.io diagram editor. Step 10 has the details.
  • MariaDB stays on 11.8, a long-term release, and MARIADB_AUTO_UPGRADE updates its system tables after an update within it. BookStack follows its releases, and updates its database by itself when it starts.
  • The uploaded images and files, and the container's web server settings, go in the bookstack-config volume; the pages themselves are in the database.

Start it:

systemctl --user daemon-reload
systemctl --user start bookstack-pod
systemctl --user enable --now podman-auto-update.timer

The first start downloads about 780 MB of images, and BookStack takes a minute to set up its database. It is ready when this prints 302:

curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1:8104/

4. Replace the default admin

BookStack creates its first account as admin@admin.com with the password password, and anyone who knows that could log in as soon as the wiki is online. Change it before you add the wiki to Caddy. An SSH tunnel does not help here: BookStack sends every link and form to https://wiki.example.com, which does not answer yet. Use BookStack's own command instead:

podman exec bookstack-app php /app/www/artisan bookstack:create-admin --initial --email=you@example.com --name="Your Name" --generate-password

--initial replaces the details of the first admin account, instead of adding another one. The command prints a new random password and nothing else. Keep it in your password manager. You can change it later under My Account > Access & Security.

5. Put Caddy in front

Go back to root with exit, switch to machinectl shell caddy@, and add this block at the end of ~/Caddyfile:

wiki.example.com {
    reverse_proxy 127.0.0.1:8104
}

Restart Caddy with systemctl --user restart caddy, and open https://wiki.example.com. Log in with your email address and the password from step 4. The old login, admin@admin.com with password, gets "These credentials do not match our records."

6. Check who can get in

Under Settings:

  • Registration: Enable registration is off, so nobody can sign up. https://wiki.example.com/register takes visitors back to the login page.
  • Features & Security: Allow public access is off, so every page needs a login.

You create accounts yourself under Settings > Users > Add New User. Until email works (step 9), switch off Send user invite email and set a password for the user.

Uploaded images are the exception to the login: a visitor who has an image's exact address can open it without logging in. BookStack does this for speed, and says so next to Enable higher security image uploads under Features & Security, which adds a random string to the names of new uploads. Keep that in mind before you upload screenshots that show passwords or customer data.

BookStack can also leave logins to a single sign-on service, such as authentik, over OpenID Connect or SAML 2.0, or to LDAP. That is set with AUTH_METHOD and the related settings in BookStack's documentation. We did not test it here.

7. Shelves, books and pages

Content is organised in four levels: shelves hold books, books hold chapters and pages. To start:

  1. Books > Create New Book, give it a name, such as Servers, and Save Book.
  2. Shelves > New Shelf, give it a name, such as IT, add the book with the + next to it under Add books to this shelf, and Save Shelf.
  3. Open the book and choose New Page. The editor saves drafts as you type.

In the editor, Insert image opens the image manager, where Upload Image adds a picture from your computer; select it and choose Select Image to put it in the page. The More button at the end of the toolbar has Insert code block, which opens a code editor with a list of languages. Save Page publishes the page.

Each save is a new revision. Set Changelog next to Save Page adds a note to it, and Revisions on the page lists every version, with Changes to compare and Restore to go back. BookStack keeps the last 100 revisions of each page by default.

The search box at the top searches the text of every page, book and shelf you are allowed to see. Export on a page or a book gives you a PDF, a web page, Markdown, plain text or a ZIP file. To write in Markdown instead, the menu next to Editing Draft in the editor has Switch to Markdown Editor.

8. Use BookStack in Swedish

BookStack comes translated into Swedish. Each user picks Svenska under My Account > Profile Details > Preferred Language, and you can set it when you create the user.

To make Swedish the default for everyone who has not picked a language, add this line to bookstack-app.container, as bookstack, and restart:

Environment=APP_LANG=sv
systemctl --user daemon-reload
systemctl --user restart bookstack-app

9. Email

BookStack sends email for invites, password resets, email confirmation on sign-up, and notifications about pages users watch. This guide does not set up email, and without it:

  • creating a user with Send user invite email on fails with "Could not create user since invite email failed to send";
  • Forgot Password? on the login page shows "An unknown error occurred" for an address that has an account;
  • Settings > Maintenance > Send a Test Email shows the error, Connection could not be established with host "localhost:587".

Accounts and logins with a password keep working.

To send email, point BookStack at an SMTP server, such as the one from the mail server guide or your email provider's. As bookstack, store the SMTP password as a secret:

printf %s 'the-smtp-password' | podman secret create bookstack-mail-password -

Then add these lines to bookstack-app.container, with your own server, account and sender address:

Environment=MAIL_DRIVER=smtp MAIL_HOST=mail.example.com MAIL_PORT=587 MAIL_ENCRYPTION=tls
Environment=MAIL_USERNAME=wiki@example.com MAIL_FROM=wiki@example.com MAIL_FROM_NAME=Wiki
Secret=bookstack-mail-password,type=env,target=MAIL_PASSWORD

Restart as in step 8, and use Send a Test Email to check it. Port 587 with MAIL_ENCRYPTION=tls uses STARTTLS; for port 465, which uses TLS from the start, change the port. We did not test sending, since this server has no mail server.

10. Outside services

With DISABLE_EXTERNAL_SERVICES=true from step 3, the server makes no outside connections in normal use. We checked by capturing its DNS lookups and outgoing connections while BookStack started, while a page was exported and while users were created. Without that line, two things change:

  • When a user is created, the server fetches a picture from www.gravatar.com, sending an MD5 hash of the user's email address. In our capture, each new user caused a lookup of www.gravatar.com and a connection to it.
  • The page editor gets a diagram button, and when a user opens it, their browser loads the draw.io editor from embed.diagrams.net.

To keep diagrams without diagrams.net, run the draw.io web app yourself and point BookStack at it, after DISABLE_EXTERNAL_SERVICES=true:

Environment=DRAWIO=https://draw.example.com/?embed=1&proto=json&spin=1&configure=1

The draw.io web app has its own image, docker.io/jgraph/drawio. We did not test that setup.

Links and embedded videos that users add to pages, such as from YouTube, load from those sites in the reader's browser, as on any web page. Webhooks, under Settings > Webhooks, call the addresses you give them.

11. Back up

As bookstack:

umask 077
mkdir -p ~/backup
podman exec bookstack-db sh -c 'mariadb-dump -ubookstack -p"$MARIADB_PASSWORD" --single-transaction bookstack' > ~/backup/bookstack-db.sql
podman volume export bookstack-config --output ~/backup/bookstack-config.tar
podman secret inspect --showsecret --format '{{.SecretData}}' bookstack-app-key > ~/backup/app-key.txt

That is what BookStack's own backup advice asks for: the database, the uploaded images and files, and the application key. The pages, users, revisions and settings are in the database, and the images and attachments in the volume. The dump runs while BookStack keeps working. Copy ~/backup to another machine, for example with restic, and store it safely: it holds all your content and the application key.

To restore into an empty pod, as bookstack, with the backup in ~/backup:

systemctl --user stop bookstack-pod
podman volume rm bookstack-db bookstack-config
podman volume create bookstack-config
podman volume import bookstack-config ~/backup/bookstack-config.tar
systemctl --user start bookstack-db
podman exec -i bookstack-db sh -c 'mariadb -ubookstack -p"$MARIADB_PASSWORD" bookstack' < ~/backup/bookstack-db.sql
systemctl --user start bookstack-app

Starting only the database first loads the backup before BookStack sets up an empty one. On a new server, create the key from the backup instead of a new one in step 2:

tr -d '\n' < ~/backup/app-key.txt | podman secret create bookstack-app-key -

12. Update

podman auto-update checks once a day for new images of BookStack and MariaDB, and restarts the pod when one has changed. BookStack updates its database when it starts. To update now:

podman auto-update

LinuxServer.io say they do not support unattended updates. If you would rather update by hand, replace latest in bookstack-app.container with a version tag, such as v26.09.1-ls287 from the image's list of tags, remove the AutoUpdate=registry line, and change the tag when you want to update. Either way, keep your backups current, and read the release notes on BookStack's blog before larger updates.

Troubleshooting

Every entry in the audit log has the server's own address. APP_PROXIES is missing or names the wrong address. It must be the server's IPv4 address, not 127.0.0.1. Fix it in bookstack-app.container, then daemon-reload and restart bookstack-app.

"Could not create user since invite email failed to send". Email is not set up. Switch off Send user invite email and set a password, or set up email as in step 9.

"An unknown error occurred" after Forgot Password?. The same cause: BookStack could not send the reset email. Settings > Maintenance > Send a Test Email shows the error.

Run it on your own server

VC-S Micro

€7.99/mo

vCPU
2
Memory
8 GB
Storage
250 GB
Transfer
10 TB
Standard
HDD · RAID 10
  • Full root access
  • Native /64 IPv6
  • RAID-protected storage
  • Malmö, Sweden
  • Month to month, no lock-in
  • 7-day money-back guarantee
All guides