If you enjoyed this article, you can support the author by becoming a sponsor on Boosty.
In one of my recent articles I talked about Booklore - a platform for managing your own web library.
In another article, dedicated to the best open-source apps of 2025, I named this app one of the discoveries of the year.
By a strange coincidence, just a couple of days before publishing the video review of this app, the author deleted the project.
What happened to Booklore: breaking down the situation#
The removal of the Booklore project came as a surprise to users, but if you look through the developer’s statements and the discussions on Reddit/GitHub, the real picture becomes clear.
The developer’s position#
The project’s author published a thread on Reddit titled:
“My side of the story, from the developer of BookLore”
It was an attempt to explain what was going on, but the key factor was not just his stance, but rather the users’ reaction.
One telling comment:
“you removed the API docs without notice”
What this actually meant:#
- changes were made without warning;
- backward compatibility was broken.
As a result, both users and integration developers suffered.
Conflict with the community#
Over time, systemic problems in the developer’s interaction with users accumulated.
The main grievances included:#
- significant changes, up to and including breaking changes, made without notice;
- functionality being removed or broken;
- feedback that was weak to the point of nonexistent;
The result was a classic open-source conflict:
developer vs. community
The abrupt disappearance of the project#
At the moment of removal, users noted:
- the GitHub repository became inaccessible (404)
- the Discord server disappeared
- there was no official announcement
This looked like a purely emotional decision - a kind of reaction to criticism that was at times more than fair - and it was ugly toward ordinary users who had no idea what was going on.
Fork and licensing issues#
An additional factor was the disputes around the license (AGPL-3.0).
What happened:#
- forks of the project appeared (for example, Grimmory, the hero of today’s article)
- possible license violations were discussed
- conflicts arose around the use of the code
The latter is actually far from rare - just look at the situation around OnlyOffice and Nextcloud.
Growing negativity around the project#
Even before the removal, there were warning signs:
- bugs
- discussions of “is it still worth using Booklore”
- criticism of architectural decisions
- questions about telemetry. Yes, there were questions about that too.
In other words, the project was already under pressure from its users.
Bottom line: the project was removed#
In fact, Booklore’s removal wasn’t caused by any single reason, but by a whole chain of events.
The main takeaway#
This isn’t the usual “the developer got burned out” story, like what recently happened with the very good project Palmr.
It’s more a case of poor communication → conflict → toxic environment → abrupt project removal.
Your humble narrator knew nothing about any of this and calmly published a video about this project on various video platforms. Naturally, people wrote in the comments telling me I was wrong, that the project was already dead, and that the video was outdated. Fine - I have no conflict with the community, so here’s a replacement for you. As I mentioned above, the original Booklore project was quickly forked by the community. Since the original Booklore was published under the AGPL license, the Grimmory project is essentially a one-to-one relicensed Booklore, just with a new name. If you look at the config files, the original docker-compose file still uses the variable name “booklore.” In any case, since I ended up in a situation where I published an article that was already outdated by the publication date, I’m fixing that now.
Below is a working version of the docker-compose file and the environment file.
services: # Section describing services (containers)
booklore: # BookLore service name
image: grimmory/grimmory:latest # BookLore Docker image from Docker Hub (latest tag)
# image: ghcr.io/booklore-app/booklore:latest
# Alternative image from GitHub Container Registry (commented out)
container_name: grimmory # Explicit container name in Docker
environment: # Container environment variables
- USER_ID=${APP_USER_ID} # UID the container runs as (file permissions)
- GROUP_ID=${APP_GROUP_ID} # GID for files and directories
- TZ=${TZ} # Container timezone
- DATABASE_URL=${DATABASE_URL} # Connection URL for the MariaDB database
- DATABASE_USERNAME=${DB_USER} # Database username
- DATABASE_PASSWORD=${DB_PASSWORD} # Database user password
- BOOKLORE_PORT=${BOOKLORE_PORT} # BookLore's internal port
depends_on: # Service dependencies
mariadb: # Depends on the mariadb service
condition: service_healthy # Start only after the DB healthcheck passes
ports:
- "${BOOKLORE_PORT}:${BOOKLORE_PORT}" # Port mapping: host → container (usually not needed when using Traefik)
volumes:
- ./data:/app/data # BookLore application data
- ./books:/books # Directory with the book library
- ./bookdrop:/bookdrop # Folder for automatic book import
healthcheck: # Container health check
test: wget -q -O - http://localhost:${BOOKLORE_PORT}/api/v1/healthcheck
# HTTP request to BookLore's built-in healthcheck API
interval: 60s # Interval between checks
retries: 5 # Number of attempts before marking the container unhealthy
start_period: 60s # Wait time before checks begin
timeout: 10s # Timeout for a single check
restart: unless-stopped # Auto-restart the container (except on manual stop)
networks:
proxy: # Connect to the external proxy network (Traefik)
labels: # Docker labels for Traefik integration
- "traefik.enable=true" # Enable Traefik handling for this container
- "traefik.http.routers.grimmory.entrypoints=web" # HTTP entrypoint (port 80)
- "traefik.http.routers.grimmory.rule=Host(`grimmory.stilicho.ru`)" # Route traffic from the grimmory.stilicho.ru domain to this container
- "traefik.http.middlewares.grimmory-https-redirect.redirectscheme.scheme=https" # Middleware to redirect HTTP → HTTPS
- "traefik.http.routers.grimmory.middlewares=grimmory-https-redirect" # Apply the redirect middleware to the HTTP route
- "traefik.http.routers.grimmory-secure.entrypoints=websecure" # HTTPS entrypoint (port 443)
- "traefik.http.routers.grimmory-secure.rule=Host(`grimmory.stilicho.ru`)" # HTTPS route for the same domain
- "traefik.http.routers.grimmory-secure.tls=true" # Enable TLS (HTTPS)
- "traefik.http.routers.grimmory-secure.service=grimmory" # Bind the HTTPS router to the booklore service
- "traefik.http.services.grimmory.loadbalancer.server.port=6060" # BookLore's internal port inside the container
- "traefik.docker.network=proxy" # Tell Traefik which Docker network to find the container in
#
mariadb: # MariaDB database service
image: lscr.io/linuxserver/mariadb:11.4.5
# MariaDB image from LinuxServer.io (stable and convenient)
container_name: mariadb # Explicit container name
environment: # MariaDB environment variables
- PUID=${DB_USER_ID} # UID of the owner of the DB files
- PGID=${DB_GROUP_ID} # GID of the owner of the DB files
- TZ=${TZ} # Timezone
- MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD} # DB root user password
- MYSQL_DATABASE=${MYSQL_DATABASE} # Name of the database created on first run
- MYSQL_USER=${DB_USER} # DB user
- MYSQL_PASSWORD=${DB_PASSWORD} # DB user password
volumes:
- ./mariadb/config:/config # Directory with MariaDB data and configuration
restart: unless-stopped # Auto-restart the container
healthcheck: # DB availability check
test: [ "CMD", "mariadb-admin", "ping", "-h", "localhost" ]
interval: 5s # Check interval
timeout: 5s # Check timeout
retries: 10 # Number of attempts
networks:
proxy: # Connect to the proxy network (Traefik)
networks:
proxy: # Define the proxy network
external: true # Network already exists and isn't created by Docker itselfEnvironment file
# =========================================================
# 🎯 BookLore - main application settings
# =========================================================
APP_USER_ID=1000
# UID of the host user that
# BookLore will run as inside the container.
# Needed for correct volume access permissions.
APP_GROUP_ID=1000
# GID of the host group.
# Must match the owner of the data / books / bookdrop directories.
TZ=Europe/Moscow
# Container timezone.
# Used for logs, schedulers, and timestamps.
BOOKLORE_PORT=6060
# Port BookLore listens on inside the container.
# Also used by Traefik as the service's internal port.
# =========================================================
# 🗄️ BookLore connection to the MariaDB database
# =========================================================
DATABASE_URL=jdbc:mariadb://mariadb:3306/grimmory
# JDBC connection string for MariaDB:
# - mariadb → service name in docker-compose
# - 3306 → default MariaDB port
# - grimmory → database name
DB_USER=grimmory
# Database user
# BookLore uses to connect to MariaDB.
DB_PASSWORD=ChangeMe_BookLoreApp_2025!
# Database user password.
# ⚠️ MUST be changed in production.
# =========================================================
# 🔧 MariaDB container settings (initialization)
# =========================================================
DB_USER_ID=1000
# UID of the user
# MariaDB writes data to the volume as.
DB_GROUP_ID=1000
# GID of the group for database files.
MYSQL_ROOT_PASSWORD=ChangeMe_MariaDBRoot_2025!
# MariaDB root user password.
# Used only for DB administration.
MYSQL_DATABASE=grimmory
# Name of the database that will be
# automatically created on the first run of the MariaDB container.



