How to Deploy Bookstack in Docker-Compose using Portainer

by Danny


Are you interested in self-hosting a Bookstack wiki using Docker? If so, look no further than this step-by-step guide.

For multi-container applications (like Bookstack), it’s easiest to use Docker-Compose. Bookstack is one of those applications that are easiest to deploy using Portainer, so in this guide I’ll show you how to:

  • Install Docker Compose
  • Deploy Portainer
  • Deploy Bookstack from within Portainer


  • You should have Docker already installed. (I have a Docker installed on Ubuntu 20.04 VM)
  • You should have an SSH tool, like Putty, installed.

If you are just starting out with Docker and don’t have it installed yet, you can follow steps 1-3 here.

Step 1: Install Docker Compose

  • To install Docker on a Ubuntu VM, follow this guide.
  • To install Docker-Compose and Portainer, follow this guide.

Once both are installed, you can continue to the next step.

Step 2: Create BookStack “Stack” in Portainer

After logging into Portainer, choose the Local tab and then select Stacks.

Give your container a name like bookstack. Then simply paste the code below.

version: "2"
    image: linuxserver/bookstack
    container_name: bookstack
      - PUID=1000
      - PGID=1000
      - DB_HOST=bookstack_db
      - DB_USER=bookstack
      - DB_PASS=yourdbpass
      - DB_DATABASE=bookstackapp
      - /srv/config/bookstack:/config
      - 6875:80
    restart: unless-stopped
      - bookstack_db
    image: linuxserver/mariadb
    container_name: bookstack_db
      - PUID=1000
      - PGID=1000
      - MYSQL_ROOT_PASSWORD=yourdbpass
      - TZ=Europe/London
      - MYSQL_DATABASE=bookstackapp
      - MYSQL_USER=bookstack
      - MYSQL_PASSWORD=yourdbpass
      - /srv/config/bookstack/DB:/config
    restart: unless-stopped

If you don’t plan on opening this up to the world and are just accessing your Bookstack wiki locally within your own network, you can leave the database user and passwords exactly as the script below.

However, there are a few lines you’ll need to edit.

  1. Type id into Putty to find your PUID and PGID. Mine is set to 1000.
  2. Change your Volume paths. Note that I have two different paths, I recommend you do the same.
  3. Change the timezone.

Step 3: Deploy the stack

The deployment will take several minutes, so just be patient here.

Once complete, click the BookStack stack. You should see two containers running:

To verify everything is setup correctly, click the Log button. You should see a systemd.done message indicating that the container is setup and ready to go.

Step 4: Open Bookstack

Bookstack should now open at

To log in, use the default credentials: and password.

Troubleshooting Bookstack in Portainer

In my case, when I deployed Bookstack via Portainer, whenever I went to it was using my public IP address instead of my internal server IP address. (I saw this in the log files of the “bookstack” container.)

If this happens to you, you need to add an APP_URL environment variable to the container.

Stop the bookstack container. Click Duplicate/Edit.

Scroll down to Env > add environment variable. Add APP_URL with a value of your server’s IP and port:

Then click Deploy the Container. This will overwrite the old container and apply the correct IP address.

After a few moments, you should be able to log into Bookstack via the server IP and port number.

Wrapping Up

I won’t go into details about how to use Bookstack as there are tons of tutorials and videos out there on that. It’s pretty self-explanatory. Considering this is my first Docker Compose stack that I’ve setup, I wanted to document the install process to make as simple for others to follow.

Hopefully this guide helps other beginners get BookStack running via Portainer!

Wrapping Up

This is a handy, simple way to be notified whenever someone logs into a server. Personally I use it for auditing logins to our Veeam backup server and domain controllers. It could also be helpful if you hire a new IT employee and are starting to delegate more privileges and you want to make sure that new hire isn’t logging into servers they aren’t supposed to yet.

My Homelab Equipment

Here is some of the gear I use in my Homelab. I highly recommend each of them.

The full list of server components I use can be found on my Equipment List page.


You may also like


jonathan January 10, 2022 - 4:26 pm

All i get is an empty web page when it loads. I see the Bookstack icon on the browser tab.

Is there something I’m missing? I followed everything on this guide down to the letter.

Danny January 10, 2022 - 4:38 pm

Does it load if you use your external IP and port?
Have you tried accessing it from an incognito browser/your mobile phone in case it’s a browser cache issue?

Jonathan January 10, 2022 - 7:37 pm

Tried, but no luck.

Jonathan January 10, 2022 - 8:03 pm

these are the logs:

[s6-init] making user provided files available at /var/run/s6/etc…exited 0.,
[s6-init] ensuring user provided files have correct perms…exited 0.,
[fix-attrs.d] applying ownership & permissions fixes…,
[fix-attrs.d] done.,
[cont-init.d] executing container initialization scripts…,
[cont-init.d] 01-envfile: executing… ,
[cont-init.d] 01-envfile: exited 0.,
[cont-init.d] 10-adduser: executing… ,
_ (),
| | ___ _ __,
| | / __| | | / \,
| | \__ \ | | | () |,
|_| |___/ |_| \__/,
Brought to you by,
To support LSIO projects visit:,,
User uid: 1000,
User gid: 1000,
[cont-init.d] 10-adduser: exited 0.,
[cont-init.d] 20-config: executing… ,
[cont-init.d] 20-config: exited 0.,
[cont-init.d] 30-keygen: executing… ,
using keys found in /config/keys,
[cont-init.d] 30-keygen: exited 0.,
[cont-init.d] 50-config: executing… ,
New container detected. Setting up app folder and fixing permissions.,
App Key found – setting variable for seds,
Running config – db_user set,
**** APP_URL in /config/www/.env is being updated from to ****,
**** If this is an existing install, you should run the following line from your host terminal to update the database URL entries: ****,
docker exec -it bookstack php /var/www/html/artisan bookstack:update-url,
/var/run/s6/etc/cont-init.d/50-config: line 97: warning: command substitution: ignored null byte in input,
/var/run/s6/etc/cont-init.d/50-config: line 97: warning: command substitution: ignored null byte in input,
Illuminate\Database\QueryException ,
SQLSTATE[HY000] [1044] Access denied for user ‘odenkaz’@’%’ to database ‘bookstackapp’ (SQL: select * from information_schema.tables where table_schema = bookstackapp and table_name = migrations and table_type = ‘BASE TABLE’),
at /var/www/html/vendor/laravel/framework/src/Illuminate/Database/Connection.php:703,
699▕ // If an exception occurs when attempting to run a query, we’ll format the error,
700▕ // message to include the bindings with SQL, which will make this exception a,
701▕ // lot more helpful to the developer instead of just the database’s errors.,
702▕ catch (Exception $e) {,
➜ 703▕ throw new QueryException(,
704▕ $query, $this->prepareBindings($bindings), $e,
705▕ );,
706▕ },
707▕ },
+33 vendor frames ,
34 /var/www/html/artisan:37,
[cont-init.d] 50-config: exited 0.,
[cont-init.d] 90-custom-folders: executing… ,
[cont-init.d] 90-custom-folders: exited 0.,
[cont-init.d] 99-custom-files: executing… ,
[custom-init] no custom files found exiting…,
[cont-init.d] 99-custom-files: exited 0.,
[cont-init.d] done.,
[services.d] starting services,
[services.d] done.,


Leave a Comment