
Run your book automation stack in Kubernetes, verify it locally, then publish only the web interface you intend to reach
Bindery is a self-hosted ebook and audiobook automation application with an embedded React interface, a Go backend, and SQLite storage. The project provides a Helm chart for Kubernetes alongside a standalone binary and container image. In this guide, we install the chart from the official repository, inspect its current configuration instead of guessing values, verify the workload and web interface, and then connect the working local service to a Localtonet HTTP tunnel. The exact chart defaults can change between releases, so the workflow deliberately discovers service names, ports, persistence settings, and image versions from the chart you actually download.
📋 What's in this guide
What you are deploying
Bindery is an automated ebook and audiobook manager designed for self-hosting. It can monitor authors, search configured indexers, send selected releases to supported download clients, organize imported media, and present library, calendar, queue, history, import, and discovery workflows through its web interface. It supports both Usenet and torrent-oriented workflows, but those integrations remain separate services that you must configure and operate according to your own environment.
The application is intentionally compact. Its official project description identifies a single Go binary with the React interface embedded into it, SQLite for application data, and no required external database, reverse proxy, or sidecar. The project also publishes an official container image and maintains a Helm chart. This makes the chart a natural option when your homelab or server environment already uses Kubernetes.
charts/bindery for deployment to Kubernetes.
Kubernetes adds scheduling, declarative configuration, restart behavior, storage abstractions, and release management around the application. It does not automatically solve persistence, backups, authentication, or safe public exposure. Those remain deployment decisions, and they should be reviewed before Bindery manages a real library.
| Component | Role | Important consideration |
|---|---|---|
| Bindery workload | Runs the application and embedded web UI | Confirm the deployed image version and pod health |
| Kubernetes Service | Provides stable in-cluster access to the workload | Discover its generated name, type, and port from your release |
| Persistent storage | Preserves SQLite data, configuration, and any chart-defined application state | Confirm the chart paths and persistence values before adding real data |
| Library storage | Makes ebook and audiobook folders available to Bindery | Mount paths must agree with download clients and import configuration |
| Download clients | Handle downloads requested by Bindery | Paths, categories, permissions, and any remapping must align |
| Localtonet client | Creates an outbound connection to our relay platform | It must run on a device that can reach the chosen local HTTP target |
Prerequisites and deployment decisions
Begin with a functioning Kubernetes cluster and administrative access appropriate for installing a namespaced Helm release. You also need the Helm and kubectl command-line tools configured for the intended cluster. This guide does not prescribe Kubernetes or Helm version numbers because the supplied Bindery evidence does not establish a compatibility matrix. Check the chart metadata and templates from the revision you plan to deploy before applying it.
You will also need Git to retrieve the official repository. If your organization installs charts only from an approved registry or a vendored source tree, adapt the retrieval step to that policy while preserving the chart inspection and verification stages.
Choose a namespace and release name
A dedicated namespace makes Bindery resources easier to inspect, upgrade, and remove. The examples below use bindery as both a user-selected namespace and release name. These are example choices, not mandatory chart defaults.
export NAMESPACE=bindery
export RELEASE=bindery
export CHART=./charts/bindery
export VALUES=bindery-values.yaml
Keep the same values throughout the workflow. If you choose different names, substitute them consistently in every Helm and kubectl command.
Plan persistent storage before installation
SQLite is an embedded database, but it is still persistent state. A pod using only ephemeral container storage can lose its database and configuration when it is rescheduled or replaced. Before treating the deployment as production-ready, determine which chart value enables persistence, where the application stores its data, which storage class will be used, and what access mode and capacity are appropriate for your cluster.
Library and download directories need separate attention. Bindery must be able to see the paths involved in scanning, importing, and organizing content. Download clients may report paths from their own container or host perspective. If their paths differ from the paths mounted into Bindery, configure the relevant mapping in Bindery rather than assuming that identically named directories exist.
The available evidence confirms that the repository contains a Helm chart, but it does not establish the current chart's volume names, mount paths, storage defaults, or value keys. Inspect the downloaded chart and its generated manifests before installation. An apparently healthy pod is not proof that SQLite data will survive replacement.
Decide how the Localtonet client will reach Bindery
A default Kubernetes ClusterIP service is normally reachable only from within the cluster network. A Localtonet client running on an unrelated desktop cannot target that address directly. For initial testing, the simplest bridge is usually a kubectl port-forward session on the same machine as the Localtonet client. For longer-running access, place the client on a device that has stable network reachability to the Kubernetes service or to another deliberately exposed local endpoint.
Do not change the Bindery Service to a publicly reachable load balancer merely to use Localtonet. Our client establishes an outbound connection to a Localtonet relay, so the local target only needs to be reachable from the device running the client.
Retrieve and inspect the official Helm chart

The project repository contains the chart source under charts/bindery. Cloning the repository makes the chart, its defaults, and its templates available for review. It also lets you pin deployment work to a known tag or commit rather than silently consuming later changes.
Clone the official Bindery repository
Retrieve the repository and enter its working directory.
Select the revision you intend to deploy
Review available tags and choose a tested release. The supplied release evidence identifies v1.37.0, but you should not assume that it remains the newest release when following this guide.
Read the chart metadata and exported values
Inspect the chart version, application version, dependencies, image configuration, service settings, persistence options, security context, probes, and resource controls available in that revision.
Create a controlled values file
Export the chart's current defaults, then change only values that are defined by the chart. Store the reviewed file securely and never add application credentials to a public repository.
Render and review the manifests
Lint the chart and render its Kubernetes resources before installation. Confirm resource names, images, services, ports, claims, mounts, and permissions from the generated output.
git clone https://github.com/vavallee/bindery.git
cd bindery
git tag --list
git status
helm show chart "$CHART"
helm show values "$CHART" > "$VALUES"
Open bindery-values.yaml in your editor. Because the exact keys are release-specific and are not established in the supplied evidence, this guide does not invent a sample values document. Use the comments and structure exported from the selected chart revision.
At a minimum, inspect the effective container image and tag, Service type and port, persistence settings, application data mounts, media mounts, pod security context, container security context, resource requests and limits, and health probes. If the chart exposes ingress options, leave them disabled unless you intentionally need a separate Kubernetes ingress path. A Localtonet tunnel does not require a public ingress resource.
helm lint "$CHART" -f "$VALUES"
helm template "$RELEASE" "$CHART" \
--namespace "$NAMESPACE" \
-f "$VALUES" > rendered-bindery.yaml
Review rendered-bindery.yaml locally. This file may contain configuration from your values file, so treat it according to the sensitivity of that configuration. Do not publish rendered manifests containing credentials or private endpoints.
A Helm chart has its own chart version and can point to a particular application image version. Inspect both before installing. Pinning the repository to an application release tag does not remove the need to verify the image rendered by Helm.
Install Bindery with Helm
Once the values and rendered resources match your environment, use Helm to create or update the release. The command below uses the namespace, release name, chart path, and values file selected earlier. It does not depend on an unverified remote chart repository.
Confirm the active Kubernetes context
Check that kubectl points to the intended cluster. Installing into the wrong context can expose data or create resources in an unintended environment.
Install the reviewed chart
Use Helm's upgrade-or-install workflow, create the selected namespace if necessary, and supply the reviewed values file.
Inspect the release status
Confirm that Helm records the release as deployed and inspect the resources created in the namespace.
Wait for the workload to become ready
Watch the pods and review events if readiness does not complete. Do not proceed to remote access until the application is stable locally.
kubectl config current-context
helm upgrade --install "$RELEASE" "$CHART" \
--namespace "$NAMESPACE" \
--create-namespace \
-f "$VALUES"
helm status "$RELEASE" --namespace "$NAMESPACE"
kubectl get all --namespace "$NAMESPACE"
kubectl get pods --namespace "$NAMESPACE" --watch
Exit the watch after the relevant pod reports a ready state. A running phase alone is not sufficient if the pod's readiness indicator remains false. Review the pod description, recent events, and logs before changing random values.
kubectl get pods --namespace "$NAMESPACE"
kubectl get services --namespace "$NAMESPACE"
kubectl get persistentvolumeclaims --namespace "$NAMESPACE"
kubectl get events --namespace "$NAMESPACE" --sort-by=.lastTimestamp
The presence of a persistent volume claim is useful evidence that the chart requested storage, but it does not by itself prove that every important application path is persistent. Compare the claim, pod mounts, and chart configuration with the data paths defined by the selected chart revision.
Verify Bindery locally before exposing it

Local verification separates application and Kubernetes problems from tunnel problems. First identify the Service generated by your release rather than assuming its name or port.
kubectl get services --namespace "$NAMESPACE"
kubectl describe service <service-name> --namespace "$NAMESPACE"
Record the Service port shown by Kubernetes. The exact Bindery listening port and Service name are deliberately not hardcoded here because they are not established by the supplied evidence and may be controlled by the chart.
Create a temporary local connection
Use kubectl port-forward to map a user-selected workstation port to the discovered Service port. In this example, 18080 is an arbitrary local port selected for the test. Replace <service-name> and <service-port> with values from your deployment.
kubectl port-forward \
--namespace "$NAMESPACE" \
service/<service-name> \
18080:<service-port>
Keep that process running and open http://127.0.0.1:18080 in a browser on the same workstation. This URL follows from the port you selected for the forwarding session. It is not a claim about Bindery's native default port.
Confirm that the web interface loads and that navigation between its principal areas works. Depending on the selected Bindery release and configured permissions, relevant pages can include the library, authors, books, series, wanted items, queue, history, import workflow, calendar, discovery, requests, and settings.
Complete the initial application configuration
Configure library roots, metadata behavior, indexers, and download clients through the options provided by the installed Bindery version. Use storage paths exactly as Bindery sees them inside its workload. A host path, a download client's path, and a Bindery container path can refer to the same data while using different strings.
Test each saved download client before initiating real downloads. Bindery v1.37.0 includes a Diagnose action for saved download clients. Its release notes state that this diagnosis checks saved settings, connectivity, category behavior, resulting ebook and audiobook paths, applicable path remapping, read and write access, and whether imports can hardlink into configured library folders. It cannot determine whether a download client can reach your indexers, so test that connection separately from the client itself.
Do not assume that a fresh Bindery installation requires a login. The project documents multiple authentication modes, and behavior can differ by configuration. Verify the active mode in your installed version, create appropriate accounts where supported, and test authorization in a private browser session before creating a public tunnel.
Test persistence deliberately
After completing a small amount of non-sensitive configuration, restart the workload through Kubernetes and confirm that the settings remain. The exact workload resource name should be discovered from the namespace.
kubectl get deployments,statefulsets --namespace "$NAMESPACE"
Use the appropriate Kubernetes restart procedure for the resource that the chart actually created. After the pod is replaced, repeat the port-forward test and confirm that configuration and application state persist. Also verify that mounted library paths remain readable and writable under the workload's runtime identity.
Routine operation, backups, and upgrades
A reliable deployment needs more than a successful first start. Keep your values file, chart revision, and application version under controlled change management. Do not place passwords, API keys, download client credentials, or authentication secrets in an unprotected repository.
Observe the release
helm list --namespace "$NAMESPACE"
helm status "$RELEASE" --namespace "$NAMESPACE"
kubectl get pods --namespace "$NAMESPACE"
kubectl get services --namespace "$NAMESPACE"
kubectl get persistentvolumeclaims --namespace "$NAMESPACE"
To inspect logs, first identify the workload or pod name. Do not assume that it matches the Helm release exactly.
kubectl get deployments,statefulsets,pods --namespace "$NAMESPACE"
kubectl logs --namespace "$NAMESPACE" <pod-name>
If a pod contains more than one container, list the pod details and select the intended container explicitly. The official project describes Bindery as not requiring sidecars, but a cluster policy or chart revision could still introduce additional containers.
Back up state and configuration
Back up the persistent application data, including the SQLite database and other state identified by the chart. Use a storage-consistent method suitable for your Kubernetes platform. Copying a live SQLite file without understanding the database's current write activity can produce an unreliable backup. A volume snapshot or application-aware procedure is preferable when supported by your storage environment.
Also retain the exact values file, selected repository revision, chart metadata, and any separately managed Kubernetes secrets required to reconstruct the deployment. Test restoration into an isolated namespace. A backup is not proven until you can restore it and open the resulting application.
Upgrade carefully
Before upgrading, read the release notes between your current and target versions, retrieve the target chart revision, export or compare its current defaults, and render the proposed manifests. Review changes to image tags, persistence, Services, selectors, probes, security contexts, and environment configuration.
helm diff upgrade "$RELEASE" "$CHART" \
--namespace "$NAMESPACE" \
-f "$VALUES"
The optional helm diff command requires the Helm diff plugin, so omit it if that plugin is not installed or approved in your environment. You can always use helm template and compare the rendered output with your currently reviewed manifests.
Take a verified backup before applying an upgrade. Then run the same helm upgrade --install command used for installation, observe rollout health, inspect logs, and repeat the browser test.
Roll back when appropriate
helm history "$RELEASE" --namespace "$NAMESPACE"
Helm can restore an earlier release revision, but a manifest rollback does not necessarily reverse an application database migration. Read the relevant Bindery release notes before relying on rollback as a database recovery strategy. Preserve backups independently of Helm's release history.
Connect the verified web UI through Localtonet

Once Bindery works through a local browser, you can expose that HTTP endpoint with Localtonet. Our client establishes an outbound connection to a Localtonet relay server, so this workflow does not require inbound router port forwarding, firewall changes, VPN setup, or a public IP address.
The Localtonet client must run on a device that can reach the chosen Bindery endpoint. For an initial test, run it on the same workstation as the active kubectl port-forward process and use 127.0.0.1 with your selected local port as the HTTP target. In the example above, that target is 127.0.0.1:18080.
Install and run the Localtonet client
Install the Localtonet application on the device that can reach the verified Bindery endpoint. Keep the application running for as long as remote access is required.
Authenticate or select the client device
Use the device-specific token associated with that client. Treat the token as a secret and never place it in screenshots, public manifests, shell history examples, or shared configuration.
Select an available relay server
Choose from the relay servers currently available in the dashboard. Available server codes and regions can vary, so obtain the value from the current product rather than copying a hardcoded example.
Create an HTTP tunnel for the local target
Point the HTTP tunnel to the IP address and port that the Localtonet client can reach. For the temporary port-forward example, use 127.0.0.1 and 18080. For a durable deployment, use the stable private endpoint available to the client device.
Start the tunnel
Creating a tunnel does not start it. Use the Start button, then confirm that the selected client is connected and the tunnel is running.
Test the assigned public address
Open the assigned public URL from a separate network or device. Confirm that Bindery loads, authentication is enforced, and only the intended interface is available. Stop or delete the tunnel when it is no longer needed.
HTTP tunnels can use a generated subdomain, a selected subdomain where supported, or a custom domain. These process types serve content at a public HTTPS address. If you need a custom domain, check the current dashboard and documentation before changing DNS because exact DNS requirements are not established in the supplied context.
A tunnel is available only while its selected Localtonet client is connected and the tunnel is running. A tunnel aimed at kubectl port-forward also depends on that port-forward process. Closing the terminal or losing cluster credentials interrupts the local target even if the tunnel configuration still exists.
Port forwarding is convenient for validation, maintenance, and occasional access. For an always-available workflow, run our client on a device with stable reachability to the Kubernetes service or another intentionally exposed private endpoint. Confirm the route from that exact device before changing the tunnel target.
Security checklist for a remotely accessible Bindery instance
Public reachability changes the risk profile of a self-hosted administration interface. Localtonet removes the need to open an inbound router port, but it does not replace application authentication or authorization. Secure Bindery itself before sharing its public address.
Bindery v1.37.0 documents a requester role that can browse the library, search for books or authors, submit requests, and follow those requests without receiving administrative actions such as deleting, grabbing, downloading, using OPDS, or seeing settings. The same release notes warn that in disabled and local-only authentication modes, a signed-out visitor can be served as an administrator. If you use requester accounts, follow the project's guidance to use an enabled or proxy authentication mode and verify the actual behavior yourself.
Do not expose download clients, SQLite files, Kubernetes dashboards, pod debugging endpoints, or library storage through the same HTTP target. The tunnel should lead only to the intended Bindery web service. Keep Kubernetes API access and cluster administration on separate, restricted paths.
Troubleshooting the deployment and tunnel
The Helm installation fails
Run helm lint again against the same chart and values file. Confirm that the current Kubernetes context is correct and that your account can create the resource types rendered by the chart. Inspect the Helm error rather than repeatedly reinstalling. Common categories include malformed values, missing custom resource definitions, policy rejection, unavailable storage classes, or insufficient permissions, but the exact cause must come from your cluster's output.
The pod remains pending
kubectl describe pod <pod-name> --namespace "$NAMESPACE"
kubectl get events --namespace "$NAMESPACE" --sort-by=.lastTimestamp
kubectl get persistentvolumeclaims --namespace "$NAMESPACE"
Look for an unbound persistent volume claim, scheduling constraints, unavailable architecture, image pull failures, or resource pressure. The project publishes ARM builds, but you must still verify that the image selected by your chart supports the architecture of the node where Kubernetes schedules it.
The pod starts and then restarts
kubectl logs <pod-name> --namespace "$NAMESPACE"
kubectl logs <pod-name> --namespace "$NAMESPACE" --previous
kubectl describe pod <pod-name> --namespace "$NAMESPACE"
Previous-container logs are especially useful after a crash. Check for invalid configuration, inaccessible mounts, read-only paths, permission failures, or probe failures. Compare the running pod specification with the rendered manifest you reviewed.
The Service exists, but the browser does not load
Confirm that the pod is ready, the Service has endpoints, and the selected Service port is correct. Then run the port-forward command in the foreground and read its error output. If 18080 is already occupied on your workstation, select another unused local port and use that same new port in the browser and Localtonet target.
kubectl get services,endpoints --namespace "$NAMESPACE"
kubectl get pods --namespace "$NAMESPACE" -o wide
The local UI works, but the public URL does not
Test in layers. Keep the browser test to 127.0.0.1 working first. Confirm that the Localtonet client is running on that same machine, the selected device is connected, the tunnel has been started, and the target uses the same IP address and local port. Creating the tunnel without starting it will not make the URL available.
If our client runs on a different device, 127.0.0.1 refers to that device, not to the workstation running kubectl port-forward. Either move the client to the forwarding workstation or provide a stable private endpoint that the client device can actually reach.
The interface loads, but downloads or imports fail
Remote access and download processing are separate paths. Check the relevant download client's connection, category, save directory, file ownership, library mounts, and path mappings. Use Bindery's Test and Diagnose functions where available. The v1.37.0 diagnosis can report the first detected problem across client settings, destination paths, permissions, and hardlink capability, but it cannot test the client's own route to its indexers.
Settings disappear after a restart
Treat this as a persistence problem. Inspect the pod's volume mounts, persistent volume claims, chart values, and application data location. Do not continue configuring the instance until data survives pod replacement. Restore from a verified backup if persistent data was lost.
Frequently asked questions
Does Bindery require PostgreSQL, MySQL, or a separate frontend?
No external database or separate frontend is identified as a requirement. Bindery is described as a single Go application with its React interface embedded and SQLite for state. SQLite data still requires durable storage and a tested backup procedure.
What is Bindery's default Kubernetes Service port?
This guide does not guess it because the supplied evidence does not establish a current default. Inspect the selected chart with helm show values, render it with helm template, and discover the deployed value with kubectl get services.
Do I need a Kubernetes Ingress to use Localtonet?
No. The Localtonet client needs a reachable local HTTP endpoint. That can be a temporary kubectl port-forward endpoint or a stable private endpoint available to the client device. A publicly reachable Kubernetes Ingress is not required for the tunnel workflow.
Does creating a Localtonet tunnel make it active immediately?
No. Creating the configuration does not mean the tunnel is running. The selected client must be connected, and you must start the tunnel. You can later stop or delete it when remote access is no longer needed.
Can the tunnel keep working after I close kubectl port-forward?
Not when the tunnel targets that forwarded port. Closing the process removes the local endpoint. For ongoing access, provide a stable private route from the device running our client to the Bindery Service or another deliberate local endpoint.
Does Localtonet replace Bindery authentication?
No. A tunnel provides network reachability. Bindery must still enforce appropriate authentication and authorization. Verify signed-out behavior and user roles before sharing the public URL.
Can I pin Bindery v1.37.0?
The official repository includes a v1.37.0 release, so you can select that repository tag for review. You must still confirm the application image rendered by the chart and test it in your cluster. Do not assume v1.37.0 remains the newest release.
Can I remove the Helm deployment without deleting my books?
That depends on how your chart revision, persistent volume claims, storage class, and media mounts are configured. Review the rendered resources and the storage reclaim behavior before uninstalling. Back up Bindery state and confirm that library data is stored independently of ephemeral pod filesystems.
Access your verified Bindery interface with Localtonet
After Bindery is healthy, persistent, and protected by appropriate authentication, run our client on a device that can reach it and create an HTTP tunnel for the verified local endpoint.
Get Started Free →