Skip to main content

bookstack-file-exporter

Table of Contents

Background

If you encounter any issues, want to request an additional feature, or provide assistance, feel free to open a Github issue.

This tool provides a way to export Bookstack pages and their content (text, images, attachments, metadata, etc.) into a relational parent-child layout locally with an option to push to remote object storage locations. See Backup Behavior section for more details on how pages are organized. Image and attachment links can also be modified in markdown and html exports to point to local exported paths.

This small project was mainly created to run as a cron job in k8s but works anywhere. This tool allows me to export my docs in markdown, or other formats like pdf. I use Bookstack's markdown editor as default instead of WYSIWYG editor and this makes my notes portable anywhere even if offline.

Features

What it does:

  • Discover and build relationships between Bookstack Shelves/Books/Chapters/Pages to create a relational parent-child layout
  • Export Bookstack pages and their content to a .tgz archive
  • Additional content for pages like their images, attachments, and metadata and can be exported
  • The exporter can also Modify Links to replace image and/or attachment links with local exported paths for a more portable backup
  • YAML configuration file for repeatable and easy runs
  • Can be run via Python or Docker
  • Can push archives to remote object storage like Minio
  • Basic housekeeping option (keep_last) to keep a tidy archive destination
  • Can run in application mode (always running) using run_interval property. Used for basic scheduling of backups.

Supported backup targets are:

  1. local
  2. minio
  3. s3 (Not Yet Implemented)

Supported backup formats are based on Bookstack API and shown here and below:

  1. html
  2. pdf
  3. markdown
  4. plaintext
  5. zip

Use Case

The main use case is to backup all docs in a relational directory-tree format to cover the scenarios:

  1. Share docs with another person to keep locally.
  2. Offline copy wanted.
  3. Back up at a file level as an accessory or alternative to disk and volume backups.
  4. Migrate all Bookstack page contents to Markdown documenting for simplicity.
  5. Provide an easy way to do automated file backups locally, in docker, or kubernetes for Bookstack page contents.

Using This Application

Ensure a valid configuration is provided when running this application. See Configuration section for more details.

Simple example configuration:

# config.yml
host: "https://bookstack.yourdomain.com"
credentials:
  token_id: ""
  token_secret: ""
formats: # md only example
- markdown
# - html
# - pdf
# - plaintext
# - zip
output_path: "bkps/"
assets:
  export_images: false
  export_attachments: false
  modify_links: false
  export_meta: false

Run via Pip

The exporter can be installed via pip (or uv) and run directly.

Python Version

Note: This application is tested and developed on Python version 3.14.5. The min required version is >= 3.11 but is recommended to install (or set up a venv) a 3.14.5 version.

Examples

python -m pip install bookstack-file-exporter

# or with uv:
uv pip install bookstack-file-exporter

# if you prefer a specific version, example:
python -m pip install bookstack-file-exporter==X.X.X

# using pip
python -m bookstack_file_exporter -c <path_to_config_file>

# if you already have python bin directory in your path
bookstack-file-exporter -c <path_to_config_file>

Options

Command line options:

option required description
-c, --config-file True Relative or Absolute path to a valid configuration file. This configuration file is checked against a schema for validation.
-v, --log-level False, default: info Provide a valid log level: info, debug, warning, error.

Environment Variables

See Valid Environment Variables for more options.

Example:

export LOG_LEVEL=debug

# using pip
python -m bookstack_file_exporter -c <path_to_config_file>

Run via Docker

Docker images are provided for linux/amd64 and linux/arm64 variants only at the moment. If another variant is required, please request it via Github Issue.

Tags

Users will generally want to use the latest tag or a specific version tag. The main tag is also provided but is not guaranteed to be stable.

tag description
latest Latest stable release and is updated with each new stable release.
X.X.X Semantic versioned releases are also provided if preferred for stability or other reasons.
main This tag reflects the main branch of this repository and may not be stable

Examples

# --user flag to override the uid/gid for created files. Set this to your uid/gid
docker run \
    --user ${USER_ID}:${USER_GID} \
    -v $(pwd)/config.yml:/export/config/config.yml:ro \
    -v $(pwd)/bkps:/export/dump \
    homeylab/bookstack-file-exporter:latest

Minimal example with object storage upload. A temporary filesystem will be used so archive will not be persistent locally.

docker run \
    -v $(pwd)/config.yml:/export/config/config.yml:ro \
    homeylab/bookstack-file-exporter:latest

Docker Compose

When using the configuration option: run_interval, a docker compose set up could be used to run the exporter as an always running application. The exporter will sleep and wait until {run_interval} seconds has elapsed before subsequent runs.

An example is shown in examples/docker-compose.yaml

Environment Variables

See Valid Environment Variables for more options.

Tokens and other options can be specified, example:

# '-e' flag for env vars
# --user flag to override the uid/gid for created files. Set this to your uid/gid
docker run \
    -e LOG_LEVEL='debug' \
    -e BOOKSTACK_TOKEN_ID='xyz' \
    -e BOOKSTACK_TOKEN_SECRET='xyz' \
    --user 1000:1000 \
    -v $(pwd)/config.yml:/export/config/config.yml:ro \
    -v $(pwd)/bkps:/export/dump \
    homeylab/bookstack-file-exporter:latest

Bind Mounts

purpose static docker path description example
config /export/config/config.yml A valid configuration file -v /local/yourpath/config.yml:/export/config/config.yml:ro
dump /export/dump Directory to place exports. This is optional when using remote storage option(s). Omit if you don't need a local copy. -v /local/yourpath/bkps:/export/dump

Run via Helm

A helm chart can be used to run the exporter as a CronJob or Deployment resource. See here for more information on using the helm chart.

Authentication and Permissions

Permissions

Note visibility of pages is based on user, so use a user that has read access to pages and content you want to back up. The role assigned to the user should have the additional permissions for target pages and their content:

  • read for all images and attachments
    • For most users this may already be set - may be required to be set depending on storage option used
  • Export Content (This can be found in Edit Role --> System Permissions)
    • For most users this may already set - may be required to be set if using custom roles
    • If not set, you may see page contents showing as a HTML login page, as reported in this issue

Token Authentication

Ref: https://demo.bookstackapp.com/api/docs#authentication

Provide a tokenId and a tokenSecret as environment variables or directly in the configuration file.

  • BOOKSTACK_TOKEN_ID
  • BOOKSTACK_TOKEN_SECRET

Env variables for credentials will take precedence over configuration file options if both are set.

For object storage authentication, find the relevant sections further down in their respective sections.

Configuration

Ensure Authentication has been set up beforehand for required credentials. For a simple example to run quickly, refer to the one in the Using This Application section.

A full example is also shown below. Optionally, look at examples/ folder of the github repo for more examples with long descriptions.

For object storage configuration, find more information in their respective sections

Schema and values are checked so ensure proper settings are provided. As mentioned, credentials can be specified as environment variables instead if preferred.

Full Example

Below is an example configuration that shows example values for all possible options.

host: "https://bookstack.yourdomain.com"
credentials:
  token_id: ""
  token_secret: ""
formats:
  - markdown
  - html
  - pdf
  - plaintext
  - zip
http_config:
  verify_ssl: false
  timeout: 30
  backoff_factor: 2.5
  retry_codes: [413, 429, 500, 502, 503, 504]
  retry_count: 5
  additional_headers:
    User-Agent: "test-agent"
minio:
  host: "minio.yourdomain.com"
  access_key: ""
  secret_key: ""
  region: "us-east-1"
  bucket: "mybucket"
  path: "bookstack/file_backups"
  keep_last: 5
output_path: "bkps/"
assets:
  export_images: true
  export_attachments: true
  modify_links: false
  export_meta: false
keep_last: 5
run_interval: 0
notifications:
  apprise:
    service_urls:
      - "json://localhost:8080/notify"
    config_path: ""
    plugin_paths: []
    storage_path: ""
    custom_title: ""
    custom_attachment_path: ""
    on_success: false
    on_failure: true
filters:
  shelves:
    exclude: ["Archive"]
  books:
    include: ["eng-.*"]
    exclude: ["draft"]
  pages:
    exclude: ["secret", "scratch"]

Options and Descriptions

More descriptions can be found for each section below:

Configuration Item Type Required Description
host str true If http/https not specified in the url, defaults to https. Use http_config.verify_ssl to disable certificate checking.
credentials object false Optional section where Bookstack tokenId and tokenSecret can be specified. Env variable for credentials may be supplied instead. See Authentication for more details.
credentials.token_id str false if specified through env var instead, otherwise true A valid Bookstack tokenId.
credentials.token_secret str false if specified through env var instead, otherwise true A valid Bookstack tokenSecret.
formats list<str> true Which export formats to use for BookStack content. Valid options are: ["markdown", "html", "pdf", "plaintext", "zip"]
export_level str false Optional (default: pages). Export granularity. See Export Level for details. Valid options: pages, books, chapters.
output_path str false Optional (default: cwd) which directory (relative or full path) to place exports. User who runs the command should have access to read/write to this directory. This directory and any parent directories will be attempted to be created if they do not exist. If not provided, will use current run directory by default. If using docker, this option can be omitted.
assets object false Optional section to export additional assets from pages.
assets.export_images bool false Optional (default: false), export all images for a page to an image directory within page directory. See Backup Behavior for more information on layout
assets.export_attachments bool false Optional (default: false), export all attachments for a page to an attachments directory within page directory. See Backup Behavior for more information on layout
assets.modify_links bool false Optional (default: false). Rewrites image and attachment URLs in markdown AND html exports to local relative paths. Requires assets.export_images and/or assets.export_attachments to be true. Only applies to markdown and html formats; pdf, plaintext, and zip are not eligible. Legacy key modify_markdown still accepted (deprecated); will be removed in a future version. See Modify Links for more information.
assets.export_meta bool false Optional (default: false), export metadata about each archived page, book, or chapter in a json file.
http_config object false Optional section to override default http configuration.
http_config.verify_ssl bool false Optional (default: false), whether or not to verify ssl certificates if using https.
http_config.timeout int false Optional (default: 30), set the timeout, in seconds, for http requests.
http_config.retry_count int false Optional (default: 5), the number of http retries after initial failure.
http_config.retry_codes List[int] false Optional (default: [413, 429, 500, 502, 503, 504]), which http response status codes trigger a retry.
http_config.backoff_factor float false Optional (default: 2.5), set the backoff_factor for http request retries. Default backoff_factor 2.5 means we wait 5, 10, 20, and then 40 seconds (with default http_config.retry_count: 5) before our last retry. This should allow for per minute rate limits to be refreshed.
http_config.additional_headers object false Optional (default: {}), specify key/value pairs that will be added as additional headers to http requests.
keep_last int false Optional (default: 0), if exporter can delete older archives. valid values are:
- set to -1 if you want to delete all archives after each run (useful if you only want to upload to object storage)
- set to 1+ if you want to retain a certain number of archives
- 0 will result in no action done.
run_interval int false Optional (default: 0). If specified, exporter will run as an application and pause for {run_interval} seconds before subsequent runs. Example: 86400 seconds = 24 hours or run once a day. Setting this property to 0 will invoke a single run and exit. Used for basic scheduling of backups.
minio object false Optional Minio configuration options.
notifications object false Optional notification configuration options.
filters object false Optional per-resource-type regex filters (include/exclude lists). See Filters for details.
filters.exclude_unassigned_books bool false Optional (default: false). When true, drop all books that are not on any shelf, independent of the books patterns. See Filters for details.

Valid Environment Variables

General

  • LOG_LEVEL: default: info. Provide a valid log level: info, debug, warning, error.

Bookstack Credentials

  • BOOKSTACK_TOKEN_ID
  • BOOKSTACK_TOKEN_SECRET

Minio Credentials

  • MINIO_ACCESS_KEY
  • MINIO_SECRET_KEY

Export Level

The export_level configuration option controls the granularity of exports:

Value Description
pages (default) One file per page. Supports assets.export_images, assets.export_attachments, and assets.modify_links.
books One combined file per book per format, written to a per-book folder (<shelf>/<book>/<book>.<ext>). Set assets.modify_links: true (with export_images/export_attachments) to download images/attachments locally and rewrite links to relative paths in markdown and html. pdf stays self-contained (assets embedded by Bookstack server-side).
chapters One combined file per chapter per format, in a per-chapter folder (<shelf>/<book>/<chapter>/<chapter>.<ext>). Same modify_links support (markdown + html) as books. Note: pages not under any chapter are not captured at this level.

Example: formats: [pdf] + export_level: books exports one PDF per book through the server-side BookStack API export.

Empty nodes: At books and chapters levels, a book or chapter with no child content is skipped — no file is written and the omission is logged at INFO. This keeps the archive free of empty placeholder documents.

The shelf/book/chapter hierarchy is preserved as directories inside the archive regardless of level — e.g. books produces <shelf>/<book>/<book>.pdf and chapters produces <shelf>/<book>/<chapter>/<chapter>.pdf (books without a shelf go under the unassigned directory).

assets.export_meta applies at all levels: when enabled, a _meta.json file is written alongside each exported node.

For non-default levels the archive filename is suffixed with the level (e.g. bkps_books_<timestamp>.tgz, bkps_chapters_<timestamp>.tgz); pages keeps the unsuffixed bkps_<timestamp>.tgz. Because keep_last cleanup matches on this prefix, archive retention is scoped independently per level.

Filters

The filters configuration option lets you include or exclude BookStack resources (shelves, books, chapters, pages) by name during export. Filtering runs during the tree build, before the exporter issues any detail API call for a node — excluded books, chapters, and pages — and all descendants of any excluded node — are never fetched.

Schema

All keys are optional. Omit a type entirely (or leave its lists empty/[]) to apply no filter for that type.

filters:
  shelves:
    exclude: ["Archive"]            # drops the Archive shelf + everything under it
  books:
    include: ["eng-.*"]             # optional allow-list (fullmatch)
    exclude: ["draft"]
  chapters:
    exclude: []
  pages:
    exclude: ["secret", "scratch"]
  # structural toggle: drop every book that is not on a shelf
  exclude_unassigned_books: false

Pattern matching

Patterns are Python re.fullmatch — the entire display name must match. Substring matching is opt-in via .*:

Pattern Matches
draft draft only
draft.* draft, draft-api
.*draft.* draft, draft-api, old-draft

This means exclude: ["draft"] drops only the resource named exactly draft, not draft-api or old-draft. Add those names to the list (or use a pattern like draft.*) to drop them too.

Patterns are also case-sensitive by default — windows does not match a shelf named Windows. Prefix with the inline flag (?i) for case-insensitive matching:

Pattern Matches
Windows Windows only
(?i)windows windows, Windows, WINDOWS
(?i).*windows.* any name containing windows, any case

What the pattern matches

Filters currently only match the resource's display name (the title shown in the BookStack UI), not the lowercased slug used for on-disk directory names. A shelf shown as Windows exports to a windows/ directory, but the filter pattern must target Windows (or (?i)windows), not the windows directory name.

Precedence

Per resource node:

  1. If include is non-empty, the name must fullmatch at least one include pattern to survive.
  2. If the name fullmatches any exclude pattern, it is dropped — exclude wins.

Both conditions are evaluated against the bare display name of the node (not a path).

Cascade

Excluding a shelf, book, or chapter prunes its entire subtree — children are never fetched or exported. For example, shelves.exclude: ["Archive"] suppresses the Archive shelf and all books, chapters, and pages beneath it with no additional configuration.

Interaction with export_level

Filters are applied as the resource tree is built, which happens before export_level selects what to archive. Shelves and books are always built (books are the basis for every level), so their filters always apply. Chapter and page filters only take effect when the level builds those nodes:

export_level Filters applied
books shelves, books
chapters shelves, books, chapters
pages (default) shelves, books, chapters, pages

A filter for a type below the selected level is silently ignored — e.g. a pages filter has no effect when export_level: books. Cascade still works at every level: excluding a parent prunes its whole subtree.

Excluding books with no shelf

A shelves filter only decides which shelves survive (and cascades to books on dropped shelves). A book on no shelf is an independent root and is not governed by the shelves filter. To drop such books, either name them with a books rule or set the structural toggle:

filters:
  shelves:
    include: ["(?i)windows"]      # keep only the Windows shelf
  exclude_unassigned_books: true  # drop every book not on a shelf

exclude_unassigned_books is structural and takes precedence over books patterns: when true, every shelfless book is dropped even if it would match a books.include pattern. books filters then only affect books that live on a surviving shelf. The toggle is independent of export_level (it applies at all levels, since books are always built).

Validation

All patterns are compiled with re.compile at config load time. An invalid regex or an empty string "" pattern is rejected with a clear error message before any API call is made.

Known limitations

  • Same-name resources cannot be individually disambiguated. If two books share the same display name, a filter pattern will match both. Use cascade (exclude the parent shelf/book) to scope the filter more precisely, or rename one of the resources.
  • Renaming a resource can silently break a filter. Filters match display names, not IDs — if a resource is renamed, existing patterns will no longer match it.
  • Shelfless books aren't governed by shelves filters — see Excluding books with no shelf.

Backup Behavior

General

Backups are exported in .tgz format and generated based off timestamp. Export names will be in the format: %Y-%m-%d_%H-%M-%S (Year-Month-Day_Hour-Minute-Second). Files are first pulled locally to create the tarball and then can be sent to object storage if needed. Example file name: bookstack_export_2023-09-22_07-19-54.tgz.

The exporter can also do housekeeping duties and keep a configured number of archives and delete older ones. See keep_last property in the Configuration section. Object storage provider configurations include their own keep_last property for flexibility.

File Naming

For file names, slug names (from Bookstack API) are used, as such certain characters like !, / will be ignored and spaces replaced from page names/titles. If your page has an empty slug value for some reason (draft that was never fully saved), the exporter will use page name with the slugify function from Django to generate a valid slug. Example: My Page.bin Name! will be converted to my-page-bin-name.

You may also notice some directories (books) and/or files (pages) in the archive have a random string at the end, example - nKA: user-and-group-management-nKA. This is expected and is because there were resources with the same name created in another shelve and bookstack adds a string at the end to ensure uniqueness.

Directory Layout

All sub directories will be created as required during the export process.

Shelves --> Books --> Chapters --> Pages

## Example
kafka (shelf)
---> controller (book)
    ---> settings (chapter)
        ---> retention-settings.md (page)
        ---> retention-settings_meta.json
            ...
        ---> compression.html (page)
        ---> compression.pdf
        ---> compression_meta.json
            ...
        ---> optional-config.md (page)
            ...
        ---> main.md (page)
            ...
---> broker (book)
    ---> settings.md (page)
        ...
    ---> deploy.md (page)
        ...
kafka-apps (shelf)
---> schema-registry (book)
    ---> protobuf.md (page)
        ...
    ---> settings.md (page)
        ...

## Example with image and attachment layout
# unassigned dir is used for books with no shelf
unassigned (shelf)
---> test (book)
    ---> images (image_dir)
        ---> test_page (page directory)
            ---> img-001.png
            ---> img-002.png
        ---> rec-page
            ---> img-010.png
            ---> img-020.png
    --> attachments (attachment_dir)
        ---> test_page (page directory)
            ---> something.config
            ---> something_else.config
        ---> rec-page
            ---> test_output.log
            ---> actual_output.log
    ---> test_page.md (page)
            ...
    ---> rec_page (page)
        ---> rec_page.md
        ---> rec_page.pdf

Another example is shown below:

## First example:
# programming = shelf
# book = react
# basics = page

bookstack_export_2023-11-28_06-24-25/programming/react/basics.md
bookstack_export_2023-11-28_06-24-25/programming/react/basics.pdf
bookstack_export_2023-11-28_06-24-25/programming/react/images/basics/YKvimage.png
bookstack_export_2023-11-28_06-24-25/programming/react/images/basics/dwwimage.png
bookstack_export_2023-11-28_06-24-25/programming/react/images/basics/NzZimage.png
bookstack_export_2023-11-28_06-24-25/programming/react/images/nextjs/next1.png
bookstack_export_2023-11-28_06-24-25/programming/react/images/nextjs/tips.png
bookstack_export_2023-11-28_06-24-25/programming/react/attachments/nextjs/sample.config
bookstack_export_2023-11-28_06-24-25/programming/react/attachments/nextjs/sample_output.log
bookstack_export_2023-11-28_06-24-25/programming/react/nextjs.md
bookstack_export_2023-11-28_06-24-25/programming/react/nextjs.pdf

Books without a shelf will be put in a shelve folder named unassigned.

Empty/New Pages

Empty/New Pages will be ignored since they have not been modified yet from creation and are empty but also do not have a valid slug.

Example from Bookstack API:

{
    ...
    "name": "New Page",
    "slug": "",
    ...
}

Images

Images will be dumped in a separate directory, images within the page parent (book/chapter) directory it belongs to. The relative path will be {parent}/images/{page}/{image_name}. As shown earlier:

bookstack_export_2023-11-28_06-24-25/programming/react/images/basics/dwwimage.png
bookstack_export_2023-11-28_06-24-25/programming/react/images/basics/NzZimage.png
bookstack_export_2023-11-28_06-24-25/programming/react/images/nextjs/next1.png
bookstack_export_2023-11-28_06-24-25/programming/react/images/nextjs/tips.png

Note you may see old images in your exports. This is because, by default, Bookstack retains images/drawings that are uploaded even if no longer referenced on an active page. Admins can run Cleanup Images in the Maintenance Settings or via CLI to remove them.

If an API call to get an image or its metadata fails, the exporter will skip the image and log the error. If using modify_links option, the image links in the document will be untouched and in its original form. All API calls are retried 3 times after initial failure.

Attachments

Attachments will be dumped in a separate directory, attachments within the page parent (book/chapter) directory it belongs to. The relative path will be {parent}/attachments/{page}/{attachment_name}. As shown earlier:

bookstack_export_2023-11-28_06-24-25/programming/react/attachments/nextjs/sample.config
bookstack_export_2023-11-28_06-24-25/programming/react/attachments/nextjs/sample_package.json
...
...

Note attachments that are just external links are ignored. Only attachments that are shown as external: False will be exported.

Reference and excerpt from Bookstack API docs:

Get a listing of attachments visible to the user. The external property indicates whether the attachment is simple a link. A false value for the external property would indicate a file upload.

If an API call to get an attachment or its metadata fails, the exporter will skip the attachment and log the error. If using modify_links option, the attachment links in the document will be untouched and in its original form. All API calls are retried 3 times after initial failure.

To use this feature, assets.export_images should be set to true and/or assets.export_attachments should be set to true.

The configuration item, assets.modify_links, can be set to true to rewrite image and attachment URL links in exported files to local relative paths. This feature makes your markdown and html exports fully portable — assets resolve locally without a network connection to the Bookstack instance.

  • Eligible formats: markdown and html only. PDF, plaintext, and zip exports are not yet requested/implemented.
  • Scope: rewrites image src attributes and their outer anchor href wrappers; rewrites attachment <a href> links. Does not rewrite inter-page, inter-book, inter-chapter, or inter-shelf links (deferred to a future issue).
  • Legacy alias: the old key modify_markdown will be removed in a future version. Rename to modify_links in your configuration.

Markdown example

Page (parent) -> Images (children) relationships are created and then each image/attachment URL is replaced with its respective local export path.

## before
[![pool-topology-1.png](https://demo.bookstack/uploads/images/gallery/2023-07/scaled-1680-/pool-topology-1.png)](https://demo.bookstack/uploads/images/gallery/2023-07/pool-topology-1.png)

## after
[![pool-topology-1.png](images/{page_name}/pool-topology-1.png)](images/{page_name}/pool-topology-1.png)

HTML example

Bookstack HTML exports wrap images in an anchor tag (click-to-zoom). Both the <img src> and the outer <a href> are rewritten to the same local file. Images appear in one of two forms; both are localized:

<!-- before: remote "scaled" thumbnail src (older bookstack installations) -->
<a href="https://demo.bookstack/uploads/images/gallery/2023-07/pool-topology-1.png">
  <img src="https://demo.bookstack/uploads/images/gallery/2023-07/scaled-1680-/pool-topology-1.png">
</a>

<!-- before: inline base64 src (recent bookstack installations) -->
<a href="https://demo.bookstack/uploads/images/gallery/2023-07/pool-topology-1.png">
  <img src="data:image/png;base64,...">
</a>

<!-- after (both forms): src and href point at the one local file -->
<a href="images/{page_name}/pool-topology-1.png">
  <img src="images/{page_name}/pool-topology-1.png">
</a>

Inline base64 images are de-inlined to the local file (shrinking the export by up to ~700 KB per full-size image). A base64 image not wrapped in a downloadable anchor is left inline (it still resolves offline).

Attachment links are rewritten from the live URL to a local relative path.

<!-- before: attachment link -->
<a href="https://demo.bookstack/attachments/42">my-config.yml</a>

<!-- after -->
<a href="attachments/{page_name}/my-config.yml">my-config.yml</a>

Known limitations

Markdown exports use raw bytes.replace — no structural awareness (e.g. DOM). If an attachment URL for some reason appears verbatim anywhere in the markdown source (code block, pre, comment, plain text), it gets replaced.

HTML exports are safe because bs4 filters to only <img src> / <a href> attributes before replacing.

Object Storage

Optionally, target(s) can be specified to upload generated archives to a remote location. Supported object storage providers can be found below:

Minio Backups

Optionally, look at examples/config.yml in the github repo, which includes a commented-out minio: block to enable object storage upload.

Authentication

Credentials can be specified directly in the minio configuration section or as environment variables. If specified in config and env, env variable will take precedence.

Environment variables:

  • MINIO_ACCESS_KEY
  • MINIO_SECRET_KEY

Example

minio:
    host: "minio.yourdomain.com"
    region: "us-east-1"
    bucket: "mybucket"
    access_key: ""
    secret_key: ""
    path: "bookstack/file_backups"
    keep_last: 5

Configuration

Item Type Required Description
host str true Hostname for minio. A host/ip + port combination is also allowed, example: minio.yourdomain.com:8443
region str true This is required since minio api appears to require it. Set to the region your bucket resides in, if unsure, try us-east-1
bucket str true Bucket to upload to
access_key str false if specified through env var instead, otherwise true Access key for the minio instance
secret_key str false if specified through env var, otherwise true Secret key for the minio instance
path str false Optional, path of the backup to use. Will use root bucket path if not set. <bucket_name>:/<path>/bookstack-<timestamp>.tgz
keep_last int false Optional (default: 0), if exporter can delete older archives in minio.
- set to 1+ if you want to retain a certain number of archives
- 0 will result in no action done

Notifications

It is possible to send notifications when an export run succeeds orfails. Currently, the only supported notification service is apprise. Apprise is a general purpose notification service and has a variety of integrations and includes generic HTTP POST.

Notifications are optional and the notification section can be omitted/removed/commented out entirely to keep a smaller configuration if not required.

The title for notifications is configurable but not if not specified, a default will be used. Example:

##### Failure Message #####
{TITLE}: Bookstack File Exporter Failed
{BODY}:
Bookstack File Exporter encountered an unrecoverable error.

Occurred At: 2025-09-06 01:02:47

Error: 401 Client Error: Unauthorized for url: https://test.bookstack/api/shelve


##### Success Message #####
{TITLE}: Bookstack File Exporter Success
{BODY}:
Bookstack File Exporter completed successfully.

Completed At: 2025-09-06 01:05:27

apprise

The apprise configuration is a part of the configuration yaml file under the notifications section and can be modified under notifications.apprise.

Item Type Description
apprise.service_urls List<str> Provide the apprise urls for apprise to send notifications to. Can also be provided as environment variable: APPRISE_URLS, see example further below.
apprise.config_path str If specified, overrides apprise.service_urls. Can specify the path to an apprise configuration file
apprise.plugin_paths List<str> Provide the plugin paths for apprise to use
apprise.storage_path str For persistent storage, specify a path for apprise to use
apprise.custom_title str Replace the default message title for apprise notifications
apprise.custom_attachment_path str To include a custom attachment to the apprise notification, specify the path to a file
apprise.on_success bool Default: false, set to true if notifications should be sent on successful export runs
apprise.on_failure bool Default: true, send notifications if run fails

apprise.service_urls can contain sensitive information and can be specified as an environment variable instead as a string list, example: export APPRISE_URLS='["json://localhost:8080/notify"]'.

If using apprise for notifications, one of apprise.service_urls or apprise.config_path should be specified.

Potential Breaking Upgrades

Below are versions that have major changes to the way configuration or exporter runs.

Start Version Target Version Description
< 1.4.X 1.5.0 assets.verify_ssl has been moved to http_config.verify_ssl and the default value has been updated to false. additional_headers has been moved to http_config.additional_headers
1.6.X vX.X.X assets.modify_markdown is deprecated — HTML image and attachment link rewrites are now supported, so the markdown-specific name no longer fits. Use assets.modify_links instead. The legacy modify_markdown key still works but will be removed in a future release.

Running Tests

This project uses uv for development. Sync dev dependencies and run the test suite:

uv sync --all-groups
uv run pytest

Or via the Taskfile target:

task test

The pytest run includes coverage by default (configured in pyproject.toml). For an HTML coverage report:

uv run pytest --cov-report=html
open htmlcov/index.html

To run only unit tests (skipping integration tests):

uv run pytest tests/unit

To run only the integration tests:

pytest -m integration

Future Items

  1. Be able to pull images locally and place in their respective page folders for a more complete file level backup.
  2. Include the exporter in a maintained helm chart as an optional deployment. The helm chart is here.
  3. Be able to modify markdown links of images to local exported images in their respective page folders for a more complete file level backup.
  4. Be able to pull attachments locally and place in their respective page folders for a more complete file level backup.
  5. Export S3 and more options.
  6. Filter shelves and books by name - for more targeted backups. Example: you only want to share a book about one topic with an external friend/user.
  7. Be able to pull media/photos from 3rd party providers like drawio

Metadata

Release files for bookstack-file-exporter 2.3.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for bookstack-file-exporter 2.3.0
File Size Uploaded
bookstack_file_exporter-2.3.0.tar.gz 68.7 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for bookstack-file-exporter 2.3.0
File Interpreter ABI Platform
bookstack_file_exporter-2.3.0-py3-none-any.whl Python 3 none any Details

Total release size: 121.6 kB

Release files / bookstack_file_exporter-2.3.0.tar.gz

Download URL bookstack_file_exporter-2.3.0.tar.gz
Size 68.7 kB
Tags Source
SHA-256 checksum
How to use checksums
bc84d347a12977fabf66c6c911413076e0d742f9f55cdcb0d20e64582b8bcc63
BLAKE2b-256 checksum
How to use checksums
f4909a06e0a3350608bbba20c487f7e6e4bd193a79bbf00c843977fdbd55ce12
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.1.0 CPython/3.13.12

Release files / bookstack_file_exporter-2.3.0-py3-none-any.whl

Download URL bookstack_file_exporter-2.3.0-py3-none-any.whl
Size 52.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
0299fe8299be143cb5ff0311b568a32257d557442706ae6b8f07efd644706085
BLAKE2b-256 checksum
How to use checksums
feb7fe6ed7e799135ce68f5eaca2ccc35aaac0f63ac6162ea2423001511bf70f
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.1.0 CPython/3.13.12

Release history Release notifications | RSS feed

3.0.0

2 release files

This release

2.3.0 This release

2 release files

2.2.0

2 release files

2.1.0

2 release files

2.0.0

2 release files

1.6.3

2 release files

1.6.2

2 release files

1.6.1

2 release files

1.6.0

2 release files

1.5.0

2 release files

1.4.1

2 release files

1.4.0

2 release files

1.3.1

2 release files

1.3.0

2 release files

1.2.0

2 release files

1.1.0

2 release files

1.0.2

2 release files

1.0.1

2 release files

1.0.0

2 release files

0.0.3

2 release files

0.0.2

2 release files

0.0.1

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page