https://github.com/asm0dey/fb2cng-web
Self-hosted web UI for fb2cng: drop fb2/fb2.zip, get EPUB/kepub/kfx/azw8/pdf back. Multi-arch (amd64+arm64) Docker image, optional Authelia forward-auth.
https://github.com/asm0dey/fb2cng-web
docker ebook ebook-converter epub fb2 fb2cng fictionbook golang kepub kindle multi-arch self-hosted
Last synced: 3 days ago
JSON representation
Self-hosted web UI for fb2cng: drop fb2/fb2.zip, get EPUB/kepub/kfx/azw8/pdf back. Multi-arch (amd64+arm64) Docker image, optional Authelia forward-auth.
- Host: GitHub
- URL: https://github.com/asm0dey/fb2cng-web
- Owner: asm0dey
- Created: 2026-06-06T15:22:43.000Z (22 days ago)
- Default Branch: main
- Last Pushed: 2026-06-24T07:05:54.000Z (4 days ago)
- Last Synced: 2026-06-26T01:30:44.578Z (3 days ago)
- Topics: docker, ebook, ebook-converter, epub, fb2, fb2cng, fictionbook, golang, kepub, kindle, multi-arch, self-hosted
- Language: Go
- Size: 106 KB
- Stars: 0
- Watchers: 0
- Forks: 0
- Open Issues: 2
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
README
# fb2cng-web
Web interface for [fb2cng](https://github.com/rupor-github/fb2cng): drop `fb2` / `fb2.zip`
files, get EPUB (or kepub/kfx/azw8/pdf) back automatically.
## Run
docker build -t fb2cng-web .
docker run --rm -p 8080:8080 fb2cng-web
> The image is built on BellSoft Alpaquita Linux (musl); the production runtime stage uses
> the hardened Alpaquita base (`bellsoft/hardened-base:musl`) — minimal, non-root (UID 65532),
> no shell or package manager. The app writes conversion temp files to `/tmp`; the hardened
> base ships a writable `/tmp`, so this works out of the box. If you run with a read-only root
> filesystem, mount a writable `/tmp` (e.g. `--tmpfs /tmp:rw,mode=1777`, as the compose example shows).
Open http://localhost:8080. Defaults work out of the box; expand **Settings** to change
format, ToC, images, footnotes, or paste/upload a full fbc YAML config.
## Versioning & images
Images are published to GitHub Container Registry on every push to `main` and
whenever a new [`fbc`](https://github.com/rupor-github/fb2cng) release appears.
Tags follow `-` (the `fbc` tag's leading `v` is dropped),
e.g. `1-1.4.5`, plus a moving `latest`:
docker pull ghcr.io//fb2cng-web:latest
docker pull ghcr.io//fb2cng-web:1-1.4.5
- **`VERSION`** holds the integer app version. Bump it by hand when the app code changes.
- **`FBC_VERSION`** holds the pinned `fbc` release. A daily GitHub Actions job
(`fbc-update`) checks upstream; on a new release it rewrites `FBC_VERSION`,
commits the bump, and publishes a fresh `-` image (and `latest`).
Images are multi-arch (`linux/amd64`, `linux/arm64`); Docker pulls the right one
automatically.
> First-time setup: the GHCR package is created on the first successful push and
> defaults to **private**. Make it public (or grant pull access) in the repo's
> Packages settings if anonymous pulls are wanted.
### Local development
Version bumps are automated with [lefthook](https://github.com/evilmartians/lefthook).
After cloning, run once:
lefthook install
Then any commit that touches app or build code (`*.go`, `go.mod`/`go.sum`,
`Dockerfile`, `internal/web/*`) auto-increments `VERSION`. Doc-, CI-, and
`FBC_VERSION`-only commits leave it untouched. The hook is local-only — it does
not run in CI, so install it after cloning.
## Configuration (env)
| Var | Default | Meaning |
|-----|---------|---------|
| `PORT` | `8080` | listen port |
| `FBC_BIN` | `fbc` | path to the fbc binary |
| `MAX_CONCURRENT` | `3` | max simultaneous conversions |
| `AUTH_FORWARD_AUTH` | `false` | trust reverse-proxy `Remote-*` headers |
| `TRUSTED_PROXIES` | (empty) | comma-separated source IPs allowed to set `Remote-*` |
> In the Docker image, `FBC_BIN` is preset to `/usr/local/bin/fbc`.
## Optional authentication (Authelia forward-auth)
The app has no built-in login. To require auth, run it behind a reverse proxy that
delegates to **Authelia**, with `AUTH_FORWARD_AUTH=true`.
> **Security:** when auth is on, never expose the app port directly. Publish only the
> proxy and keep the app on an internal network. Optionally set `TRUSTED_PROXIES` so the
> app ignores `Remote-*` headers from any other source.
Authelia forward-auth endpoint: `/api/authz/forward-auth`. Copy headers
`Remote-User`, `Remote-Groups`, `Remote-Email`, `Remote-Name` to the app.
### Caddy (`Caddyfile`)
```caddyfile
fb2.example.com {
forward_auth authelia:9091 {
uri /api/authz/forward-auth
copy_headers Remote-User Remote-Groups Remote-Email Remote-Name
}
reverse_proxy fb2cng-web:8080
}
```
### Traefik (dynamic config)
```yaml
http:
middlewares:
authelia:
forwardAuth:
address: "http://authelia:9091/api/authz/forward-auth"
authResponseHeaders:
- "Remote-User"
- "Remote-Groups"
- "Remote-Email"
- "Remote-Name"
routers:
fb2:
rule: "Host(`fb2.example.com`)"
middlewares: ["authelia"]
service: fb2cng-web
services:
fb2cng-web:
loadBalancer:
servers:
- url: "http://fb2cng-web:8080"
```
### Nginx Proxy Manager (Proxy Host → Advanced tab)
```nginx
location /authelia {
# nginx uses Authelia's auth-request endpoint (not the forward-auth one used by Caddy/Traefik)
internal;
proxy_pass http://authelia:9091/api/authz/auth-request;
proxy_pass_request_body off;
proxy_set_header Content-Length "";
proxy_set_header X-Original-URL $scheme://$http_host$request_uri;
proxy_set_header X-Original-Method $request_method;
proxy_set_header X-Forwarded-For $remote_addr;
}
location / {
auth_request /authelia;
auth_request_set $user $upstream_http_remote_user;
auth_request_set $groups $upstream_http_remote_groups;
auth_request_set $name $upstream_http_remote_name;
auth_request_set $email $upstream_http_remote_email;
proxy_set_header Remote-User $user;
proxy_set_header Remote-Groups $groups;
proxy_set_header Remote-Name $name;
proxy_set_header Remote-Email $email;
error_page 401 =302 https://auth.example.com/?rd=$scheme://$http_host$request_uri;
proxy_pass http://fb2cng-web:8080;
}
```