Moving My E-books to Booklore
Last weekend, a blog post about Booklore crossed my feed and piqued my interest.
I've long used Calibre to manage my e-books and, in June last year, even containerised Calibre-server so that I could read (parts of) my library in a web browser.
As fantastic as Calibre is, its interface is showing its age and I don't love that they've been diverting energy into adding AI features. I'm sure that they're probably useful to someone, but I tend to view reading as an escape rather than an opportunity to further immerse myself in industry hype.
Booklore though, looks shiny and new.
One of its features - Kobo sync support - also caught my eye. This masquerades as the Kobo store, allowing physical e-readers to fetch books remotely.
This post talks about getting Booklore up and running and playing around with it - it details some of the niggles that I ran into as well as some of the tweaks that I've made.
* * *
### Deploying
Booklore's docs provide an example docker-compose.
Although I used it for a quick initial look, it wasn't really suitable for my longer term needs: I have a system specifically dedicated to databases, so needed to break the compose up across two different hosts.
* * *
#### Database - MariaDB
On my database host, I populated a `.env` file with the variables specific to the database:
TZ=Etc/UTC
# BookLore DB related
BOOKLORE_DB_USER=booklore
BOOKLORE_DB_PASSWORD=ChangeMe_BookLoreApp_2025!
BOOKLORE_DB_USER_ID=1000
BOOKLORE_DB_GROUP_ID=1000
BOOKLORE_MYSQL_ROOT_PASSWORD=ChangeMe_MariaDBRoot_2025!
BOOKLORE_MYSQL_DATABASE=booklore
Note: passwords were also changed.
I then added a service to my `docker-compose`:
services:
mariadb:
image: lscr.io/linuxserver/mariadb:11.4.5
container_name: mariadb
environment:
- PUID=${BOOKLORE_DB_USER_ID}
- PGID=${BOOKLORE_DB_GROUP_ID}
- TZ=${TZ}
- MYSQL_ROOT_PASSWORD=${BOOKLORE_MYSQL_ROOT_PASSWORD}
- MYSQL_DATABASE=${BOOKLORE_MYSQL_DATABASE}
- MYSQL_USER=${BOOKLORE_DB_USER}
- MYSQL_PASSWORD=${BOOKLORE_DB_PASSWORD}
ports:
- 3306:3306
volumes:
- /home/ben/docker_files/files/booklore_mariadb/config:/config
restart: unless-stopped
healthcheck:
test: [ "CMD", "mariadb-admin", "ping", "-h", "localhost" ]
interval: 5s
timeout: 5s
retries: 10
Aside from splitting the config, I deviated from the documented example in a couple of small ways:
* All of the env vars are prefixed with `BOOKLORE_`: my `docker-compose` isn't specific to Booklore so I namespaced variables to prevent them affecting other services (whether future or current)
* The volume uses an absolute path
* The MariaDB port (3306) is exposed to the network
I will also, at some point, move from a Booklore dedicated MySQL to using a single shared instance. As I wasn't yet sure whether Booklore was going to be right for me I decided to keep the dedicated (and therefore easily disposable) container.
I did actually look into whether it'd be possible to do away with MariaDB entirely: Booklore's config includes a JDBC URL, suggesting that an ORM is in use. Given the choice, I'd have preferred to use a single container with a self contained SQLite instance.
However, a quick hunt around indicated that this isn't currently possible.
* * *
#### Booklore
With MariaDB up and running, I moved onto configuring and deploying Booklore itself.
Booklore includes a feature called Bookdrop. This is a filesystem location monitored with something like `inotify`, so when new files appear, they're automatically queued for import.
I wanted to be able to easily drop books into the Bookdrop so decided to use a NFS share (note: Bookdrop's docs include a warning about this - I'll come back to this as I noticed it later).
I updated `.env` (once again, ensuring all variables were prefixed with `BOOKLORE_`):
# BookLore Settings
BOOKLORE_APP_USER_ID=0
BOOKLORE_APP_GROUP_ID=0
BOOKLORE_PORT=6060
BOOKLORE_DATABASE_URL=jdbc:mariadb://192.168.13.22:3306/booklore
BOOKLORE_DB_USER=booklore
BOOKLORE_DB_PASSWORD=ChangeMe_BookLoreApp_2025!
I then updated my `docker-compose` to define both the service and the volume:
services:
booklore:
image: booklore/booklore:latest
container_name: booklore
environment:
- USER_ID=${BOOKLORE_APP_USER_ID}
- GROUP_ID=${BOOKLORE_APP_GROUP_ID}
- TZ=${TZ}
- DATABASE_URL=${BOOKLORE_DATABASE_URL}
- DATABASE_USERNAME=${BOOKLORE_DB_USER}
- DATABASE_PASSWORD=${BOOKLORE_DB_PASSWORD}
- BOOKLORE_PORT=${BOOKLORE_PORT}
ports:
- "${BOOKLORE_PORT}:${BOOKLORE_PORT}"
volumes:
- /home/ben/docker_files/files/booklore/data:/app/data
- /home/ben/docker_files/files/booklore/books:/books
- type: volume
source: bookdrop
target: /bookdrop
volume:
nocopy: true
restart: unless-stopped
volumes:
bookdrop:
driver_opts:
type: "nfs"
o: "nfsvers=4,addr=192.168.13.233,nolock,soft,ro"
device: ":/volume1/bookdrop"
One again, there are a couple of small deviations from the documented compose:
* NFS volume defined for Bookdrop
* Other volumes use absolute paths
* * *
### First Configuration
After I'd started services on each box (with `docker compose up -d`), Booklore started listening on port `6060`.
I visited `http://192.168.13.25:6060` which displayed a prompt to configure the initial admin user:
After hitting `Create Admin Account` the system made me log in with the credentials that I'd just defined, ultimately delivering me to Booklore's dashboard:
While looking over the interface I felt a moment of instant revulsion as my eyes passed over this icon:
Thankfully, despite looking **a lot** like the icon used for various AI offerings, this actually links to Booklore's metadata management dashboard.
One thing that I did notice in the settings, is that Booklore has telemetry enabled by default:
The project document the telemetry well and it does seem to be fairly un-intrusive, but I still didn't love that it was on by default1.
* * *
#### Creating An Unprivileged User
I tend to try and avoid using privileged accounts whenever possible.
So, I tried to create myself an unprivileged user for day to day use.
However, my first attempt failed: users must be assigned at least one accessible "Library" and none exist by default:
I'll talk a little more about creating a library in the next section.
Booklore has reasonably granular access permissions which can be set while creating a user:
It wasn't _entirely_ clear which privileges I'd need, so I took a rough guess and reasoned that I could come back and tweak them later.
* * *
#### Creating A Library
Booklore allows you to create multiple Libraries to upload books into. Users can be granted access to specific libraries, allowing you to (theoretically) keep users away from collections of books that aren't relevant (or perhaps even appropriate).
The library creation dialog requires you to specify a name and pick an icon2:
The next stage (`Directories`) requires you to specify the filesystem path that ebooks in the library should be saved to.
This introduced a small niggle: the filesystem browser doesn't provide the ability to _create_ directories.
In my `docker-compose` I'd bound a single volume to `/books` and so wanted my various libraries to be created underneath that.
With the interface unable to let me create a new subdirectory, I had to SSH into the host and create it from there
mkdir ~/docker_files/files/booklore/books/default_library
This was then available for selection in the interface:
It's a bit of a pain, but the need to deal with is likely be to quite rare.
* * *
### Using BookDrop
Before switching to my new unprivileged user, I decided to try Bookdrop.
I mounted the NFS share on my laptop, dropped an ebook into it and headed to the Bookdrop menu.
Nothing happened...
Bookdrop's docs do actually contain a warning about this3:
> Bookdrop may not reliably detect new files on network-mounted storage (NFS, SMB). If files aren't detected, use a local folder, restart BookLore, or click the manual **Refresh** button in the Bookdrop UI.
It's perfectly reasonable really (although the button is now labelled `Rescan`).
Clicking `Rescan` caused the test ebook to appear:
The idea is that you allocate each of the books to a library and review the metadata that's been extracted (or, in some cases, fetched).
There's a `Finalize` button at the bottom of the screen which commits the changes.
* * *
#### Copying From Calibre
The Calibre library on my laptop isn't huge at just under 600 books, so I decided to throw caution to the wind and copy all ebooks from it into the Bookdrop share:
find ./Calibre\ Library/ \
-type f \
-regex '.*\(epub\|mobi\|azw3\|pdf\)$' \
-exec cp {} ~/bookdrop/ \;
One quick `Rescan` in the Bookdrop interface and they appeared.
However, my Calibre library contained duplicates - at some point I've presumably needed a `.mobi` rather than an `.epub` and Calibre has helpfully kept both.
Each of these showed up independently in Bookdrop, so I had to work through clearing out duplicates (the Bookdrop screen has a `delete` button, so it wasn't too much work).
All in all, the import required about 10 minutes of manual effort - far less than if I'd had to individually upload each book.
* * *
### Un-Privileged Usage
I switched to using my unprivileged user.
This immediately led to another _tiny_ niggle: the system forces new users to change their password from the one that was set when creating their user.
In a multi-user system, that makes sense: the admin knows the password that they set for the user so it should be changed. It makes much less sense when there's one human involved, but it's a temporary and tiny irritation.
Once logged in, the dashboard showed the single book in the library:
I decided that I'd create a new "private" library, but ran into the next small problem.
New Libraries are added by clicking the cross next to `Libraries`:
However, clicking it did _nothing_.
This turned out to be because I hadn't given my unprivileged user the (rather broad sounding) `Manage Libraries` permission.
Ultimately, I decided that library creation should be quite a rare operation and decided to reserve that capability for the admin user.
* * *
#### Direct Uploading
The UI also provides an option to upload books directly into a chosen library:
I used this to upload a book from a system that didn't have access to the Bookdrop NFS share.
However, there was a small wrinkle in the user experience here too.
The upload writes the book file (i.e. the `epub`) directly into the Library location on the filesystem and then indicates that the upload was successful:
However, that's _all_ that it does: the uploaded book doesn't actually appear in the user's library:
This was pretty confusing. At first, I thought that I must've missed something and so tried re-uploading, but that failed:
I checked the Bookdrop view in case I'd misunderstood the flow, but there was no sign of the book.
Eventually, I figured it out: the issue is that Booklore doesn't trigger a rescan of the library after the upload. To make things worse, this was hidden from the unprivileged user because the menu that is used to trigger a re-scan wasn't available.
If we look at the same library, but logged in as `admin` there's a three dot menu after the library name:
This felt like significant enough of a UI failing that others should have noticed it too. But, I realised that it essentially boiled down to two choices that I'd made
* The user doesn't have `Manage Library` privileges
* The library doesn't have `Monitor Folders` enabled, so doesn't auto-rescan if new books are added on the filesystem4
I didn't _particularly_ want to change either of those, so this wrinkle posed a bit more of a problem than some of the others.
However, it's proven to be quite easy to work around: the file upload dialog allows the user to upload to Bookdrop instead, so I can upload to that and _then_ assign to a Library.
Although it is an extra step, it isn't particularly onerous and is something that I can live with.
* * *
### Magic Shelves
Booklore has a function called Magic Shelves.
This creates the book equivalent of smart playlists: you define some query parameters and it dynamically populates the shelf with books matching those criteria:
This seemed like a great way to discover books within our collection, so I enthusiastically created multiple magic shelves
* Pratchett
* Me!
* Unread (Fiction)
* Unread (Non Fiction)
* In Progress
* Gave Up On
However, after creating them I realised that the shelves weren't really listed in the most convenient order: they appear in order of creation but, really, I at least wanted "In Progress" at the top.
Unfortunately, Booklore does not currently provide a way to reorder them.
I _could_ have deleted and recreated them, but instead I decided to throw caution to the wind and make changes directly in the database:
describe magic_shelf;
+-------------+--------------+------+-----+---------------------+-------------------------------+
| Field | Type | Null | Key | Default | Extra |
+-------------+--------------+------+-----+---------------------+-------------------------------+
| id | bigint(20) | NO | PRI | NULL | auto_increment |
| user_id | bigint(20) | NO | MUL | NULL | |
| name | varchar(255) | NO | | NULL | |
| icon | varchar(64) | NO | | NULL | |
| filter_json | longtext | NO | | NULL | |
| created_at | timestamp | YES | | current_timestamp() | |
| updated_at | timestamp | YES | | current_timestamp() | on update current_timestamp() |
| is_public | tinyint(1) | NO | | 0 | |
| icon_type | varchar(100) | NO | | PRIME_NG | |
+-------------+--------------+------+-----+---------------------+-------------------------------+
I had been expecting that there'd be a `sort` or `order` column, but there wasn't.
A quick query of the data showed that the shelves were indeed displaying in the order of their primary key (column `id`):
select * from magic_shelf;
+----+---------+----------------------+---------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------+---------------------+---------------------+-----------+-----------+
| id | user_id | name | icon | filter_json | created_at | updated_at | is_public | icon_type |
+----+---------+----------------------+---------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------+---------------------+---------------------+-----------+-----------+
| 1 | 2 | Pratchett's | pi pi-align-justify | {"type":"group","join":"and","rules":[{"field":"authors","operator":"includes_any","value":["Terry Pratchett"]}]} | 2026-02-22 12:47:15 | 2026-02-22 12:48:01 | 0 | PRIME_NG |
| 2 | 2 | Me! | pi pi-bookmark-fill | {"type":"group","join":"and","rules":[{"field":"authors","operator":"includes_any","value":["B Tasker","Ben Tasker"]}]} | 2026-02-22 14:09:25 | 2026-02-22 14:09:25 | 0 | PRIME_NG |
| 3 | 2 | Unread (Fiction) | pi pi-circle-off | {"type":"group","join":"and","rules":[{"field":"readStatus","operator":"not_equals","value":"READ"},{"field":"library","operator":"excludes_all","value":[4,3,5,6]}]} | 2026-02-22 14:10:43 | 2026-02-22 14:10:43 | 0 | PRIME_NG |
| 4 | 2 | Unread (Non Fiction) | pi pi-circle-off | {"type":"group","join":"and","rules":[{"field":"readStatus","operator":"not_equals","value":"READ"},{"field":"library","operator":"excludes_all","value":[1,4,5]}]} | 2026-02-22 14:11:27 | 2026-02-22 14:11:53 | 0 | PRIME_NG |
If I wanted to reorder items, I'd need to change the value of `id`. I had a quick poke through the rest of the database to check that the column wasn't used as a foreign key anywhere (it doesn't seem to be) and then moved the first two items down:
update magic_shelf set id=8 where id=2;
update magic_shelf set id=9 where id=1;
This freed up a couple of slots so that I could move others up:
update magic_shelf set id=2 where id=6;
The result of this (somewhat risky) operation was that Booklore's web interface displayed the shelves in my preferred order:
* * *
### Reverse Proxy
Booklore was looking pretty hopeful, so I decided that it was time to add it to my reverse proxy - allowing clients to connect via HTTPS:
server {
listen 443 ssl;
root /usr/share/nginx/empty;
index index.php index.html index.htm;
server_name ebooks.example.com;
ssl_certificate /etc/letsencrypt/live/ebooks.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/ebooks.example.com/privkey.pem;
ssl_session_timeout 5m;
large_client_header_buffers 8 32k;
location / {
proxy_pass http://192.168.13.25:6060;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header Host $http_host;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Port $server_port;
proxy_hide_header X-Powered-By;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
client_max_body_size 20m;
proxy_buffer_size 128k;
proxy_buffers 4 256k;
proxy_busy_buffers_size 256k;
add_header X-Clacks-Overhead "GNU Terry Pratchett";
include /etc/nginx/auth.include;
}
}
There's something important to note here.
Booklore's documentation suggests that you need to include the following:
proxy_buffer_size 128k;
proxy_buffers 4 256k;
proxy_busy_buffers_size 256k;
large_client_header_buffers 8 32k;
I've included _most_ of these within my `location` block, but `large_client_header_buffers` appears earlier in the config. That directive is scoped to `http` or `server` levels, so Nginx will fail to start if it appears within a `location` context.
* * *
### Tuning Memory Usage
While talking about Booklore in the fediverse, someone mentioned that they were disappointed with the amount of memory that it consumes.
Unfortunately, they're not wrong:
As wonderful as Booklore seems to be, it is written in Java and therefore consumes quite a disproportionate amount of memory5.
Unfortunately, the demand of LLMs seems to have forcibly evicted us from the age of cheap memory, so having a (relatively) rarely used app sit on ~1GiB of RAM is less than ideal.
Although there isn't much that a user can do about the amount of memory that the application _requires_ , we can influence how quickly/easily that memory is given back to the OS once no longer in active use.
I decided to try and bring idle usage down by
* setting the garbage collector to the Garbage 1st Garbage Collector (this _should_ actually be the default, but best to be explicit)
* tweaking tunables to influence how much memory the GC tried to free
* tweaking heap size
This can be achieved by passing additional arguments to the `java` invocation but, in a docker container, is more easily done via environment variable:
environment:
- JAVA_OPTS="-XX:+UseG1GC -XX:MaxHeapFreeRatio=45 -XX:MinHeapFreeRatio=20 -Xms256m -Xmx1500m -XX:+UseStringDeduplication -XX:InitiatingHeapOccupancyPercent=30"
Breaking that down:
* `-XX:+UseG1GC`: use the Garbage 1st GC
* `-XX:MaxHeapFreeRatio=45`: The GC should only allow unused memory to make up 45% of the heap - anything over that will be returned to the operating system
* `-XX:MinHeapFreeRatio=20`: The GC should try and ensure the heap is at least 20% free memory (we don't want to set this too low otherwise the app will block waiting on allocations from the OS)
* `-Xms256m`: At startup, the heap should be allocated 256MB
* `-Xmx1500m`: The maximum size of the heap should be 1.5GB, Anything over this will result in allocation errors
* `-XX:+UseStringDeduplication`: Identify duplicate strings in memory and update them to point to a single reference
* `-XX:InitiatingHeapOccupancyPercent=30`: Kick off marking cycles once the heap reaches 30% utilisation
Nothing too scientific went into arriving at these figures. The max heap size was selected based on the peak consumption in my graphs. The free ratios were arrived at through trial and error - adjusting until monitoring graphs started to show a pattern of freeing and then reallocating.
This helped _a bit_ :
The base usage is still higher than I'd like and probably can't be tweaked all that much further, but it does at least free _some_ additional memory up.
Unfortunately, the application's usage **does** seem to grow with library size, so this might become a more pressing issue in future.
* * *
### Reading
I know, I've waffled on for 3500 words and haven't talked _once_ about what the reading experience is like.
So far, I've read books (well, mostly _parts_ of books) from Booklore in a handful of ways.
* * *
#### Desktop: Booklore Web Interface
As a rule, I don't generally read from my desktop, however I _do_ sometimes use it to access work related books.
The reason that I find a desktop beneficial for work books specifically, is that it allows for easy typing of notes.
It's hard to offer much critique on the reading view, it does what it should:
* * *
#### Mobile: Booklore Web Interface
Booklore has a responsive UI, so it's comfortably possible to also use it via my phone's web browser.
This is also how I previously used Calibre's web interface - I saved the bookmark to my home screen as a web-app.
However, this method of access is of limited use for offline reading. Much like Calibre's web interface, there seems to be some level of support for offline mode but I've previously found that my phone sometimes insists on trying to reload instead.
* * *
#### Mobile: FBReader
I've had FBReader on my phone since I first moved away from Kindle.
Booklore supports OPDS so FBReader can use that to connect and download books, providing the offline access that my browser based approach lacks.
Unfortunately, there are some drawbacks
* reading progress and annotations do not get synced back
* FBReader doesn't seem to store credentials, so you have to log in each time
While functional, the interface is also probably not to everyone's taste:
* * *
#### Mobile: Readest
Readest supports both OPDS and KOReader Sync, allowing it to sync reading progress back to Booklore.
Although not a major selling point for me, the interface is also much nicer looking:
The only real drawback is that the process to enable KOReader sync is quite unituitive. Although there's a `Sign into Sync` option on the app's main menu, that's for Readest's sync server - instead you have to go to the book listing view and open a menu there to locate the `KOReader Sync` option.
It's a bit of a pain, but it _is_ also only a one-time thing.
One small downside: although progress is synced, notes and annotations are not. That probably doesn't matter too much for me though, as my phone is much more likely to be used for recreational reading than it is for work.
* * *
#### Device: Kobo
Kobo sync support is the reason that Booklore caught my attention in the first place, so I was keen to get this up and running.
One thing to note is that, the way that Kobo sync works isn't quite the same as for readers using OPDS: Booklore's Kobo integration doesn't provide access to the entire library, but instead to books assigned to a `Kobo` shelf.
I **think** that I like this: it provides much more granular control over what's taking up the device's storage.
* * *
##### Kobo Configuration
I _had_ intended to set this up on an old Kobo Glo, but it turned out to be (properly) dead. Instead, I bought myself a birthday present: a Kobo Clara Colour.
As well as being my first colour e-ink device, this also came with the advantage of it never having been linked to a Kobo account, so that I could see what it was like starting from scratch.
I didn't want to have to register it with Kobo _at all_ and remembered that Neil has previously written about bypassing registration on Kobos:
cd /media/ben/KOBOeReader/.kobo
sqlite3 KoboReader.sqlite "INSERT INTO user(UserID,UserKey) VALUES('1','');"
I then connected it to our Wifi network and waited while it downloaded updates.
Finally, I reconnected to my PC so that I could configure it to talk to Booklore:
cd /media/ben/KOBOeReader/.kobo/Kobo
nano Kobo\ eReader.conf
The instructions say to scroll down to `[OneStoreServices]` and then update the value of `api_endpoint` in that section, however the config file didn't contain `api_endpoint` at all
$ grep api_endp Kobo\ eReader.conf
So, I added it
[OneStoreServices]
api_url=https://ebooks.example.com/api/kobo/<your-token-here>
With no idea whether this was actually going to work, I went to Booklore, assigned a book to the Kobo shelf and then hit "Sync" on the Kobo
It failed:
My Nginx access logs proved the Kobo was talking to Booklore, but something was wrong.
Unfortunately, it turns out that Booklore proxies some of the API endpoints onto Kobo's store, so you do need to have linked a Kobo account.
On the Kobo I hit "Accounts" and signed out of the fake account. This fully reset the Kobo - I even had to reconnect it to our wifi network.
It also overwrote `Kobo eReader.conf` so, after I'd linked it to a Kobo account, the device stopped trying to communicate with Booklore.
This time, though, there **was** an api_endpoint entry in the file:
api_endpoint=https://storeapi.kobo.com
After updating it to point back to Booklore, the Kobo displayed my chosen book:
Disappointingly, although reading progress is synced back to Booklore it's not shared with the web UI's reader, so progress in one doesn't advance the other (however, in the time it's taken me to play around with this, they've cut new releases which appear to fix this).
* * *
### Still A Place For Calibre
It's worth noting that, despite the effort that I've gone into setting Booklore up, there's still very much a place for Calibre in my toolkit.
As a rule, I don't generally _buy_ DRM encumbered e-books:
Even though I occasionally buy them, I never **read** DRM encumbered e-books. If I been unable to avoid buying one, the very first thing that I do is strip the DRM. If it can't be stripped, then I actively chase a refund6.
On it's own, Calibre is no more able to load DRM'd ebooks than Booklore is. However, there are third party plugins available to address this.
On the (rare) regrettable occasion that I have to buy a DRM'd copy, I'll use Calibre to strip it before adding into Booklore.
* * *
### Update Booklore
While proof-reading this post, I noticed that Booklore have cut a few releases since I started playing around with it - this post was written using version 1.18.5 but a new 2.x branch has been released this week.
Updating was as simple as pulling the latest container image and then restarting services:
docker pull booklore/booklore:latest
docker compose up -d
My install is now running version 2.0.3.
Sure enough, it brings the ability to have two-way progress sync between the Kobo and other reading apps
After turning this on, the Kobo prompted to ask whether I wanted to jump to where I'd got to on another device.
* * *
### Conclusion
Booklore is now up and running and playing host to our full collection of ebooks.
There have been a few more niggles than I was expecting, but they're mostly quite minor (I should probably think about chucking in PRs for some of them) and aren't really out of keeping with how young Booklore still is as a project.
The main remaining niggle is RAM usage: at times the container is using 1.25GiB of RAM. For what amounts to an API exposing ebooks, that's a pretty significant chunk of memory.
But, the box that Booklore is running on isn't currently too memory constrained. Hopefully, future Booklore updates will help pull consumption down.
The system itself works well, books sync from Booklore to Readest and my Kobo. Now all that I have to do, is find time to read them.
* * *
1. I appreciate that there's an argument that telemetry _needs_ to be on by default to be useful, but I self-host OSS for a reason and don't love phone-home functionality. ↩
2. Having to choose an icon is a bit of a nuisance, albeit a very minor one ↩
3. Which I'd have seen if I hadn't insisted on setting this up without RTFM ↩
4. It's off by default and I'd left it that way because I'm not planning to drop books directly into the library storage ↩
5. At the risk of kicking off language wars, it reminds me a little of when Redmine was first launched: excitement at what the software offers mixed with sadness at the drawbacks of the language it was written in ↩
6. I know it's not really the author's fault, it tends to be publishers who insist on DRM - often the same orgs who think that students should have to go hungry in order to be able to afford a textbook - but I'm not paying for a crippled product, if I can't be sure that I'll always be able to read it how and when I want, then, frankly, you can keep it. ↩