Your personal comic library server
Host your digital comics (CBZ/CBR/CB7/PDF files) on your home server and read them on any device. Issued automatically organizes your collection and makes it available through mobile apps like Panels, Chunky, or a built-in web reader.
- π Keep your folder structure β No need to reorganize your comics
- π Automatic updates β New comics are detected and added automatically
- πΌοΈ Thumbnail previews β See cover images before opening
- π± Read anywhere β Use your favorite comic reader app (Panels, Chunky, etc.)
- π Web reader β Read in your browser without installing anything (on mobile too!)
- π Light and dark themes β Follow your system preference or switch manually
- π Ongoing series β Mark active series and quickly see new issues, totals, and possible gaps
βΆοΈ Continue a series β Resume the latest issue in progress or start the next unread issue- β‘ Easy setup β One command to get started
Issued works with all common digital comic formats:
- CBZ (Comic Book ZIP) - ZIP archives containing images
- CBR (Comic Book RAR) - RAR archives containing images (requires
unrar) - CB7 (Comic Book 7-Zip) - 7-Zip archives containing images
- PDF - PDF documents with comic pages
Issued detects the real container from the file contents. Misnamed archives, such
as a .cbz file that is actually compressed with 7-Zip, are handled automatically.
All formats support:
- β Automatic page extraction and rendering
- β Thumbnail generation from first page
- β Metadata extraction (ComicInfo.xml or PDF metadata)
- β Full compatibility with OPDS readers and web reader
- β Reading progress tracking
- Browse your library β Navigate folders and series, with cover previews and a recent additions feed.
- Search β Find comics by filename or metadata, with OpenSearch discovery for compatible apps.
- Download originals β Download complete comic archives or PDFs.
- Page streaming (OPDS-PSE 1.2) β Read individual pages in compatible apps without downloading the whole comic. Existing OPDS download links remain available.
PSE pages are served as JPEG. Existing JPEGs are passed through unchanged; other page images are converted on the fly at high quality, without resizing or modifying the original files. Streaming pages does not update reading progress.
Docker users: skip this section β the Docker image includes everything you need.
For CBR files only (skip if you only have CBZ or PDF):
Install unrar on your computer:
- macOS:
brew install rar - Linux:
sudo apt install rar - Windows: Download from rarlab.com
Note: CBZ, CB7, and PDF files work without any extra software. CBR files need
unrarto extract.
Best for: Running on a home server or NAS. Everything is pre-configured, including unrar.
Step 1: Create a docker-compose.yml file anywhere on your server:
services:
issued:
image: ghcr.io/metalogico/issued:latest
restart: unless-stopped
ports:
- "8181:8181"
volumes:
- ./data:/app/data
- /path/to/your/comics:/comics:ro # β Change this pathChange /path/to/your/comics to your comics folder:
- /home/user/Comics:/comics:ro # Linux
- /Users/john/Comics:/comics:ro # macOS
- C:/Comics:/comics:ro # WindowsStep 2: Start the server
docker compose up -dIf the comics folder is not mounted yet (for example the NAS is still coming up), Issued waits and leaves the database untouched. The web UI and OPDS catalog start only after the library is visible.
Done! Your comics are now available at:
- π± Mobile apps:
http://YOUR-SERVER-IP:8181/opds/ - π Web reader:
http://YOUR-SERVER-IP:8181/reader/
Replace
YOUR-SERVER-IPwith your computer's IP address (e.g.,192.168.1.100)
Alternative: docker run
docker run -d --name issued \
--restart unless-stopped \
-p 8181:8181 \
-v /path/to/your/comics:/comics:ro \
-v issued_data:/app/data \
ghcr.io/metalogico/issued:latestAlternative: docker create (create now, start later)
docker create \
--name=issued \
--user 1000:1000 \
-p 8181:8181 \
--mount type=bind,source=/path/to/your/comics,target=/comics,readonly \
--mount type=bind,source=/path/to/data,target=/app/data \
--restart unless-stopped \
ghcr.io/metalogico/issued:latestThen start it:
docker start issuedThe image is also available on Docker Hub as metalogico/issued.
Best for: Quick setup on your personal computer.
Windows note: Put the executable in a writable folder, such as your Desktop or another folder inside your user profile. Avoid
C:\Program Filesunless you run it with permissions to write there, because Issued storesconfig.ini,library.db, logs, and thumbnails beside the executable by default.
Step 1: Download for your system
Get the latest release from GitHub Releases:
- macOS:
issued-macos - Linux:
issued-linux - Windows:
issued.exe
Step 2: Make it executable (macOS/Linux only)
chmod +x issued
# macOS only: If blocked by security
xattr -d com.apple.quarantine issuedStep 3: Initialize your library
# Replace with your actual comics folder path
./issued init --library /path/to/your/comicsExamples:
./issued init --library ~/Comics # macOS/Linux
./issued init --library C:\Users\You\Comics # WindowsStep 4: Scan your comics
./issued scanThis will:
- Find all CBZ/CBR/CB7/PDF files
- Create thumbnails
- Build the library database
Step 5: Start the server
./issued serveDone! Your comics are now available at:
- π± Mobile apps:
http://YOUR-COMPUTER-IP:8181/opds/ - π Web reader:
http://YOUR-COMPUTER-IP:8181/reader/
Tip: Keep the terminal window open while the server runs. New comics will be detected automatically.
Popular apps that work with Issued:
- Panels (iOS) - Best overall experience
- Chunky Reader (iOS) - Great for iPad
- KyBook (iOS) - Feature-rich
- Moon+ Reader (Android) - Highly customizable
- And many oders with opds support (please let me know what you use and I'll add it here)
How to connect:
- Open your comic reader app
- Add a new OPDS catalog
- Enter:
http://YOUR-IP:8181/opds/ - Browse, search, and download comics!
Compatible OPDS clients discover library search automatically via OpenSearch.
Issued supports OPDS Page Streaming Extension (OPDS-PSE) 1.2 in folder, recent, and search feeds. This extends the existing Atom OPDS catalog; it does not add OPDS 2. Clients can read or download individual pages without downloading the original comic archive.
- Endpoint:
GET /opds/comic/{comic_uuid}/page/{page_number}. - Page numbering is 0-based, from
0toN-1. The web reader API remains 1-based at/reader/api/comic/{comic_uuid}/page/{page_num}. - Every successful PSE response is
image/jpeg. Original JPEG bytes are preserved; other supported page images (including PNG and WebP, and rendered PDF pages) are converted with Pillow at quality 95, without chroma subsampling or resizing. Transparency is composited on white; animated images use their first frame. Conversion can introduce JPEG compression loss. - The feed uses the scanned database page count. If it is missing or invalid, the entry retains its existing links but omits PSE until a scan records a positive count. Building feeds never opens archives to count pages. A direct page request uses the actual archive contents and does not update the count.
- Streaming, preloading, and downloading pages never save reading progress.
Existing Issued progress APIs remain responsible for that. No
pse:lastRead,pse:lastReadDate, or{maxWidth}capability is advertised.
Example link for a 24-page comic (the feed declares
xmlns:pse="http://vaemendis.net/opds-pse/ns"):
<link rel="http://vaemendis.net/opds-pse/stream"
type="image/jpeg"
href="https://issued.example.com/opds/comic/abc123/page/{pageNumber}"
pse:count="24" />Access: the page endpoint follows the existing OPDS access model: it is accessible without a reader session cookie, including when reader password protection is enabled. It never redirects to the reader login. Any access control provided by your reverse proxy continues to apply. Public URLs use the same forwarded host/protocol configuration described below.
Responses use Cache-Control: private, max-age=3600. Missing comics/files and
out-of-range pages return 404; non-integer page parameters return 422; unreadable
archives/images return 500; unavailable extraction backends return 503. Errors
are JSON and do not include filesystem paths. No persistent page cache is added.
See the OPDS-PSE 1.2 specification.
Finding your IP:
- macOS/Linux: Run
ifconfigorip addr- Windows: Run
ipconfig- Look for something like
192.168.1.100
Just open: http://localhost:8181/reader/
From the web reader you can browse folders, search your library, read comics, edit comic details, and keep track of what you are reading.
The reader's scripts and styles are served with Cache-Control: no-cache: the browser keeps its copy but checks it with the server on each load, so an update takes effect without a forced refresh.
- Use Recent to see the comics you added most recently.
- Use Continue Reading to jump back into comics you have started but not finished.
- Mark comics as done from the grid or table view.
- When you are inside a series folder, use Mark all as completed if you want to mark the whole series as done.
- In a series folder, use Continue series to resume the most recently opened unfinished issue or start the first unread one. The reader also links to the previous and next issue and offers the next cover when you reach the end.
If you follow series that are still coming out, Issued can keep them together for you.
- Open the series folder in the web reader.
- Click the Ongoing button.
- Use Ongoing in the top menu to see all the series you are tracking.
The Ongoing page shows the latest added issue, how many issues are in the series, and possible missing issue numbers. The button appears on series folders that contain comics directly.
Optional: Add password protection by editing config.ini:
[reader]
user = yourname
password = yourpasswordJust copy them to your comics folder! If the server is running, they'll be detected automatically.
You can also start a scan from the web reader: click the refresh icon in the top menu. Issued will scan your library, show how many comics were added, updated, or deleted, and then refresh the page.
Or manually scan from the command line:
./issued scan./issued scan --forceIf you really emptied the comics folder and want the database to drop those entries, use ./issued scan --prune. Without --prune, Issued will refuse to wipe the library when the folder looks unmounted or empty.
./issued stats./issued thumbnailsOnly comics without a thumbnail β or whose thumbnail file is missing β are processed.
./issued thumbnails --regenerateEdit config.ini in the app data folder. For standalone executables this is the
same folder as the executable unless you set DATA_DIR; for Docker it is
/app/data inside the container, usually backed by your mounted volume.
- Change port:
[server]βport = 8080 - Add password:
[reader]βuser = name,password = pass - Adjust thumbnails:
[thumbnails]βwidth,height,quality
Edit config.ini for more options:
[library]
path = /path/to/comics # Your comics folder
name = My Comics # Library name shown in apps
[server]
host = 0.0.0.0 # Listen on all network interfaces
port = 8181 # Change if port is already in use
[thumbnails]
width = 300 # Thumbnail width in pixels
height = 450 # Thumbnail height in pixels
quality = 85 # JPEG quality (1-100)
format = jpeg # jpeg or webp
[monitoring]
enabled = true # Auto-detect new comics
debounce_seconds = 2 # Wait time before processing changes
[reader]
user = # Leave empty for no password
password = # Leave empty for no passwordIssued keeps its two public route prefixes when served through a reverse proxy:
- Web reader:
https://issued.example.com/reader/ - OPDS catalog:
https://issued.example.com/opds/
Do not strip or rewrite /reader or /opds. For example, with nginx:
server {
listen 443 ssl;
server_name issued.example.com;
location / {
proxy_pass http://issued:8181;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}Reader assets and images use origin-relative URLs, so they work with either an
IP address or a domain. OPDS clients require absolute URLs; forwarding Host
and X-Forwarded-Proto ensures those URLs contain the public domain and
protocol.
When nginx, Caddy, Traefik, or another proxy runs in a separate container, add
FORWARDED_ALLOW_IPS=* to the Issued container environment, or replace *
with the proxy network/IP range. This lets Uvicorn trust the forwarded protocol.
-
Check the scan worked:
./issued stats
Should show your comic count.
-
Try a forced rescan:
./issued scan --force
-
Use your computer's IP address, not
localhost- Find it:
ifconfig(Mac/Linux) oripconfig(Windows) - Example:
http://192.168.1.100:8181/opds/
- Find it:
-
Check firewall:
- Make sure port 8181 is allowed
- macOS: System Settings β Network β Firewall
- Windows: Windows Defender Firewall β Allow an app
-
Make sure both devices are on the same network
- Computer and phone/tablet must be on the same WiFi
Install unrar:
- macOS:
brew install unrar - Linux:
sudo apt install unrar - Windows: Download from rarlab.com
Then restart the server.
xattr -d com.apple.quarantine issuedEdit config.ini and change the port:
[server]
port = 8080Then use http://YOUR-IP:8080/opds/ instead.
Docker: It runs in the background automatically.
Executable: Run in the background:
# macOS/Linux
nohup ./issued serve &
# Or use screen/tmux
screen -S issued
./issued serve
# Press Ctrl+A then D to detachThis project uses the violet accent color just because my baby daughter is called Violet <3
If you're stuck:
- Check the Issues page
- Open a new issue with:
- Your OS (macOS/Linux/Windows)
- How you installed (Docker/executable)
- What you tried
- Error messages (if any)
MIT. See LICENSE for details.