OpenPanelOpenPanel

Documentation

Everything you need to install, configure, and run OpenPanel.

Quick Start

The fastest way to get running is Docker Compose. Clone the repository and start:

Terminal
git clone https://github.com/73gon/openpanel.git
cd openpanel

Open docker-compose.yml and update the volume mount to point to the folder where your manga/comic CBZ files live. For example, if your files are at /home/user/manga, replace the placeholder path with that.

Terminal
docker compose up -d

Open http://localhost:3001 in your browser. You'll be prompted to create an admin account โ€” after that, add your library path and OpenPanel will scan, generate thumbnails, and be ready instantly.

Library Structure

Organize your CBZ files in folders. Each top-level subfolder becomes a series. CBZ files placed directly in the root become standalone books.

๐Ÿ“manga/
๐Ÿ“One Piece/
๐Ÿ“„Chapter 001.cbz
๐Ÿ“„Chapter 002.cbz
๐Ÿ“„...
๐Ÿ“Naruto/
๐Ÿ“Vol 01/
๐Ÿ“„Chapter 001.cbz
๐Ÿ“„Chapter 002.cbz
๐Ÿ“Vol 02/
๐Ÿ“„Chapter 003.cbz
๐Ÿ“„Standalone Book.cbz

In this example, the root folder is your library path โ€” the one you mount in Docker (e.g. /libraries/manga) or set via the admin panel. Inside it:

  • One Piece/ becomes a series with chapters detected from filenames
  • Naruto/ shows nested volumes โ€” OpenPanel supports any depth of nesting and auto-detects chapter boundaries
  • Standalone Book.cbz appears as its own single-volume series

Renaming or reorganizing folders triggers an automatic re-scan (file system watcher). You can also trigger a manual scan from the admin panel.

Docker

Build the Docker image and run it directly:

Terminal
docker build -t openpanel .

Start a container. Replace /path/to/manga with the absolute path to your manga/comic folder on the host machine. The :ro flag mounts it read-only โ€” OpenPanel never modifies your files.

Terminal
docker run -d \
  -p 3001:3001 \
  -v openpanel-data:/data \
  -v /path/to/manga:/libraries/manga:ro \
  --name openpanel \
  openpanel

openpanel-data is a named Docker volume for the SQLite database and thumbnail cache. Your data persists across container restarts.

Docker Compose

The repository includes a ready-to-use docker-compose.yml. Update the volume paths to match your system:

docker-compose.yml
services:
  openpanel:
    build: .
    ports:
      - "3001:3001"
    volumes:
      - openpanel-data:/data
      - /home/user/manga:/libraries/manga:ro
      - /home/user/comics:/libraries/comics:ro
    restart: unless-stopped

volumes:
  openpanel-data:

The paths before the colon (e.g. /home/user/manga) are your host paths. The paths after the colon (e.g. /libraries/manga) are what OpenPanel sees inside the container โ€” these are the paths you'll enter in the admin panel.

Terminal
docker compose up -d

HTTPS with Caddy

The repo includes a Caddyfile for automatic HTTPS via Let's Encrypt. Three steps:

  1. Open Caddyfile and replace openpanel.example.com with your actual domain
  2. In docker-compose.yml, uncomment the caddy service block
  3. Run docker compose up -d โ€” Caddy obtains a TLS certificate automatically

Caddy also handles automatic certificate renewal. No manual SSL configuration required.

Linux Installation

Build and run natively without Docker. You'll need Rust 1.75+ and Node.js 20+ (or Bun).

Build the frontend

Terminal
cd ui
npm install
npm run build

This outputs the compiled frontend to ui/dist/. The backend serves these files automatically.

Build the backend

Terminal
cd server
cargo build --release

Run

Set environment variables for the data directory and port, then start the server:

Terminal
OPENPANEL_DATA_DIR=/var/lib/openpanel \
OPENPANEL_PORT=3001 \
./target/release/openpanel-server

OPENPANEL_DATA_DIR is where the SQLite database and thumbnail cache are stored. Create this directory beforehand and ensure the running user has write permissions.

systemd service (auto-start on boot)

Create a service file so OpenPanel starts automatically:

/etc/systemd/system/openpanel.service
[Unit]
Description=OpenPanel manga reader
After=network.target

[Service]
Type=simple
User=openpanel
WorkingDirectory=/opt/openpanel/server
ExecStart=/opt/openpanel/server/target/release/openpanel-server
Environment=OPENPANEL_PORT=3001
Environment=OPENPANEL_DATA_DIR=/var/lib/openpanel
Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target

Enable and start the service:

Terminal
sudo systemctl daemon-reload
sudo systemctl enable --now openpanel

Windows Installation

Option 1: Docker Desktop

Install Docker Desktop for Windows, then clone and start:

PowerShell
git clone https://github.com/73gon/openpanel.git
cd openpanel

Edit docker-compose.yml and use Windows-style paths for your volumes. In the example below, replace C:/Users/you/manga with the actual folder where your CBZ files are stored:

docker-compose.yml
services:
  openpanel:
    build: .
    ports:
      - "3001:3001"
    volumes:
      - openpanel-data:/data
      - C:/Users/you/manga:/libraries/manga:ro
    restart: unless-stopped

volumes:
  openpanel-data:
PowerShell
docker compose up -d

Option 2: Build from Source

You'll need Rust 1.75+ (via rustup), Node.js 20+ (or Bun), and the Visual Studio C++ Build Tools.

Build the frontend

PowerShell
cd ui
npm install
npm run build

Build the backend

PowerShell
cd server
cargo build --release

Run

Set environment variables and start the server:

PowerShell
$env:OPENPANEL_DATA_DIR="C:\ProgramData\openpanel"
$env:OPENPANEL_PORT="3001"
.\target\release\openpanel-server.exe

C:\ProgramData\openpanel is a good location for the data directory on Windows. Ensure it exists before starting.

Windows Service (auto-start on boot)

Use NSSM to register OpenPanel as a Windows service:

PowerShell
winget install nssm

Register and configure the service:

PowerShell
nssm install OpenPanel "C:\openpanel\server\target\release\openpanel-server.exe"
nssm set OpenPanel AppDirectory "C:\openpanel\server"
nssm set OpenPanel AppEnvironmentExtra OPENPANEL_PORT=3001 OPENPANEL_DATA_DIR=C:\ProgramData\openpanel

Start the service:

PowerShell
nssm start OpenPanel

Manage with nssm stop OpenPanel, nssm restart OpenPanel, or through the Windows Services panel (services.msc).

Configuration

Configure via environment variables or a .env file placed in the server/ directory:

VariableDefaultDescription
OPENPANEL_PORT3001HTTP server port
OPENPANEL_DATA_DIR./dataSQLite database + thumbnail storage
DATABASE_URLsqlite://<DATA_DIR>/openpanel.dbSQLite connection URL
OPENPANEL_LIBRARY_ROOTS(empty)Comma-separated library paths to scan on startup
OPENPANEL_DEV_MODEfalseEnable CORS for localhost dev servers
OPENPANEL_LOG_LEVELinfoTracing log level (debug, info, warn, error)
OPENPANEL_ZIP_CACHE_SIZE200ZIP index LRU cache size
OPENPANEL_PUBLIC_URLhttp://localhost:3001Public URL (used for CORS)
OPENPANEL_SCAN_ON_STARTUPtrueAuto-scan libraries on server start

Example .env

Place this file in server/.env. The server loads it automatically on start:

.env
OPENPANEL_PORT=3001
OPENPANEL_DATA_DIR=./data
OPENPANEL_DEV_MODE=true
OPENPANEL_LIBRARY_ROOTS=/home/user/manga,/home/user/comics
OPENPANEL_LOG_LEVEL=info

First-Run Setup

  1. Open the app in your browser. You'll see a Create Admin Account form. Enter a username and password โ€” this becomes the admin user.
  2. After logging in, click the shield icon in the sidebar to open the admin panel.
  3. In Libraries, click Add Library and enter the path to your manga folder. If using Docker, enter the container path (e.g. /libraries/manga), not the host path.
  4. Click Scan Now. OpenPanel indexes every CBZ file, generates thumbnails, and detects chapters.
  5. Go back to Home โ€” your series are ready to read.

Updating

Docker

Terminal
docker compose pull
docker compose up -d

Linux (native)

Pull the latest code, rebuild both frontend and backend, then restart:

Terminal
git pull
cd ui && npm ci && npm run build && cd ..
cd server && cargo build --release && cd ..
sudo systemctl restart openpanel

Windows (native)

PowerShell
git pull
cd ui; npm ci; npm run build; cd ..
cd server; cargo build --release; cd ..
nssm restart OpenPanel

In-app update

Admin users can check for updates and trigger an update from the admin panel. The server downloads the latest release, replaces the binary, and restarts automatically.

PWA Installation

Mobile (iOS / Android)

  1. Open your OpenPanel URL in Safari (iOS) or Chrome (Android)
  2. Tap Share (iOS) or the three-dot menu (Android)
  3. Select Add to Home Screen

Desktop (Chrome / Edge)

  1. Open your OpenPanel URL
  2. Click the install icon in the address bar

What you get

  • Offline shell โ€” the UI loads instantly even on slow connections
  • API caching โ€” series lists cached with Network-First (5-minute expiry)
  • Page caching โ€” pages you've read are cached locally (up to 500, 7-day expiry)
  • Auto-updates โ€” the service worker updates automatically on new deployments

Offline Downloads

When using OpenPanel as an installed PWA, you can download chapters and volumes for offline reading. Downloads are stored locally in your browser using IndexedDB.

How it works

  • PWA only โ€” download buttons appear only when the app is installed (Add to Home Screen). Browser users are prompted to install first.
  • Per-page storage โ€” each page is fetched and stored as a blob in IndexedDB, along with metadata (title, page count, size).
  • Persistent storage โ€” on the first download, the app requests navigator.storage.persist() to protect your downloads from browser eviction.
  • Downloads tab โ€” a dedicated Downloads tab in the mobile navigation lets you manage all downloaded content and see storage usage.

Downloading

  1. Open a series detail page on mobile
  2. Tap the download icon on any chapter/volume, or use Download All in the section header
  3. Progress is shown per-item during download
  4. Downloaded items show a green checkmark

Managing downloads

  • Go to the Downloads tab (mobile nav) to see all downloaded content
  • Delete individual downloads or clear all at once
  • Storage usage is displayed at the bottom of the downloads page

API Reference

All routes are under /api/. Auth routes are public; most others require a Bearer token in the Authorization header.

MethodPathDescription
GET/api/healthHealth check
POST/api/auth/registerRegister a new user
POST/api/auth/loginLogin (returns token)
POST/api/auth/logoutLogout (invalidates token)
GET/api/auth/meCurrent user info
GET/api/auth/statusSetup status
GET/api/librariesList all libraries
GET/api/seriesList series (sort, genre, status)
GET/api/series/:id/booksBooks in a series
GET/api/series/:id/chaptersDetected chapters
GET/api/series/:id/metadataAniList metadata
GET/api/genresList all genres
GET/api/books/:idBook details
GET/api/books/:id/pages/:numStream a page image
GET/api/books/:id/thumbnailBook thumbnail (WebP)
GET/api/series/:id/thumbnailSeries thumbnail (WebP)
GET/PUT/api/progressReading progress
GET/api/continue-readingContinue reading list
GET/POST/api/bookmarksList/create bookmarks
DELETE/api/bookmarks/:idDelete a bookmark
GET/POST/api/collectionsList/create collections
POST/api/collections/:id/itemsAdd series to collection
GET/PUT/api/preferencesUser preferences
GET/api/versionServer version
POST/api/admin/scanTrigger library scan
POST/api/admin/librariesAdd a library
GET/api/admin/profilesList users
PUT/api/admin/profiles/:id/reset-passwordReset user password (admin)
POST/api/admin/backupCreate backup
POST/api/admin/updateTrigger server update

Architecture

OpenPanel has three main components:

Frontend

React SPA

Vite PWA

Backend

Axum (Rust)

REST API

Database

SQLite

WAL mode

  • Backend โ€” Rust + Axum 0.8 + SQLite (sqlx). Serves API and static frontend.
  • Frontend โ€” React 19 + TypeScript + Vite 7 + TanStack Router + Zustand + Tailwind v4.
  • CBZ reading โ€” ZIP central directory parsed once, cached in LRU. Pages served by seek โ€” no extraction.
  • Auth โ€” Bcrypt passwords, 1-year server-side sessions, Bearer tokens.
  • Metadata โ€” AniList integration cached in SQLite.

Uninstalling

Docker

Stop and remove the containers, then optionally delete the data volume:

Terminal
docker compose down

This stops the containers but keeps your data volume. To also delete the database and thumbnails:

Terminal
docker compose down -v

To remove the Docker image as well:

Terminal
docker rmi openpanel

Linux (native)

Stop and disable the service, then remove the files:

Terminal
sudo systemctl stop openpanel
sudo systemctl disable openpanel
sudo rm /etc/systemd/system/openpanel.service
sudo systemctl daemon-reload

Delete the application and data directories:

Terminal
sudo rm -rf /opt/openpanel
sudo rm -rf /var/lib/openpanel

Windows (native)

If running as a service via NSSM:

PowerShell
nssm stop OpenPanel
nssm remove OpenPanel confirm

Then delete the application and data folders:

PowerShell
Remove-Item -Recurse -Force C:\openpanel
Remove-Item -Recurse -Force C:\ProgramData\openpanel

Your manga/comic files are never modified โ€” only the database and thumbnail cache in the data directory are removed.

Password Reset

If you've forgotten the admin password, you have several options depending on your access level.

Option 1: Admin resets another user's password

If an admin account is accessible, go to Admin โ†’ Profiles and click the lock icon next to the user. Enter a new password (minimum 4 characters). The user's existing sessions are invalidated immediately.

Option 2: Users change their own password

Any logged-in user can change their password from the Profile page. Enter your current password, then set a new one.

Option 3: Delete and re-create the admin user

Stop the server, then delete the admin user from the database. On next startup with no users, the app will show the "Create Admin Account" screen again.

Terminal
sqlite3 data/openpanel.db "DELETE FROM profiles WHERE role = 'admin';"

Restart the server and create a new admin account in the browser.

Option 4: Generate a new bcrypt hash

If you want to keep the admin user's progress and bookmarks, generate a new bcrypt password hash and update the record:

Terminal
htpasswd -nbBC 12 "" "newpassword" | tr -d ':\n' | sed 's/$2y/$2b/'

Then update the database with the generated hash:

Terminal
sqlite3 data/openpanel.db "UPDATE profiles SET password_hash = '<paste_hash>' WHERE username = 'admin';"

Restart the server for changes to take effect.

Password Management

OpenPanel provides multiple ways to manage passwords depending on your role.

Changing your own password

Navigate to your Profile page (bottom nav on mobile, sidebar on desktop). Enter your current password and set a new one. Minimum password length is 4 characters.

Resetting another user's password (Admin)

  1. Go to Admin โ†’ Profiles
  2. Click the lock icon next to the user
  3. Enter a new password (minimum 4 characters)
  4. Click Reset Password

The user's existing sessions are invalidated immediately. They will need to log in again with the new password. All password changes are logged in the admin audit log.

Reverse Proxy

If you already have nginx, Traefik, or another reverse proxy, you can route traffic to OpenPanel's port. Here's an example nginx config:

nginx.conf
server {
    listen 443 ssl http2;
    server_name openpanel.example.com;

    ssl_certificate     /etc/letsencrypt/live/openpanel.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/openpanel.example.com/privkey.pem;

    location / {
        proxy_pass http://127.0.0.1:3001;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

Replace openpanel.example.com with your domain. If using WebSocket features in the future, add the Upgrade headers as well.

Set OPENPANEL_PUBLIC_URL in your environment to the public URL (e.g. https://openpanel.example.com) so CORS works correctly.

Troubleshooting

Port already in use

If port 3001 is taken, change the port via the OPENPANEL_PORT environment variable:

Terminal
OPENPANEL_PORT=8080 ./target/release/openpanel-server

For Docker, update the port mapping in docker-compose.yml:

docker-compose.yml (snippet)
ports:
  - "8080:3001"

Changing the library path after setup

Go to the admin panel โ†’ Libraries. You can add new library paths or remove existing ones. After adding a new path, click Scan Now to index it.

For Docker, you also need to add a new volume mount in docker-compose.yml and restart the container.

Migrating data to a new server

Copy the entire data directory (<DATA_DIR>/) to the new server. This contains the SQLite database (users, progress, bookmarks, library index) and thumbnail cache.

On the new server, set OPENPANEL_DATA_DIR to point to the copied directory. Ensure your library paths still resolve to the same CBZ files โ€” if the mount paths differ, update them in the admin panel.

Fixing a corrupted database

If the server fails to start with SQLite errors, try running an integrity check:

Terminal
sqlite3 data/openpanel.db "PRAGMA integrity_check;"

If corruption is found, you can attempt a recovery by exporting and reimporting:

Terminal
sqlite3 data/openpanel.db ".recover" | sqlite3 data/openpanel-recovered.db
mv data/openpanel.db data/openpanel-corrupt.db
mv data/openpanel-recovered.db data/openpanel.db

As a last resort, delete the database and let OpenPanel re-create it. You'll lose user accounts and reading progress, but a library re-scan will restore the book index.

Thumbnails not generating

Ensure the data directory has write permissions. Thumbnails are stored in <DATA_DIR>/thumbnails/. Check logs with OPENPANEL_LOG_LEVEL=debug for detailed error messages.

Series not appearing after scan

Verify your library path is correct โ€” for Docker, the path must match the container mount (e.g. /libraries/manga), not the host path. Check that your files have the .cbz extension and are valid ZIP archives.

High memory usage

The ZIP index cache defaults to 200 entries. If you have a very large library and limited RAM, lower it:

.env
OPENPANEL_ZIP_CACHE_SIZE=50