Everything you need to install, configure, and run OpenPanel.
The fastest way to get running is Docker Compose. Clone the repository and start:
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.
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.
Organize your CBZ files in folders. Each top-level subfolder becomes a series. CBZ files placed directly in the root become standalone books.
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:
Renaming or reorganizing folders triggers an automatic re-scan (file system watcher). You can also trigger a manual scan from the admin panel.
Build the Docker image and run it directly:
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.
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.
The repository includes a ready-to-use docker-compose.yml. Update the volume paths to match your system:
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.
docker compose up -d
The repo includes a Caddyfile for automatic HTTPS via Let's Encrypt. Three steps:
Caddyfile and replace openpanel.example.com with your actual domaindocker-compose.yml, uncomment the caddy service blockdocker compose up -d โ Caddy obtains a TLS certificate automaticallyCaddy also handles automatic certificate renewal. No manual SSL configuration required.
Build and run natively without Docker. You'll need Rust 1.75+ and Node.js 20+ (or Bun).
cd ui npm install npm run build
This outputs the compiled frontend to ui/dist/. The backend serves these files automatically.
cd server cargo build --release
Set environment variables for the data directory and port, then start the server:
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.
Create a service file so OpenPanel starts automatically:
[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:
sudo systemctl daemon-reload sudo systemctl enable --now openpanel
Install Docker Desktop for Windows, then clone and start:
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:
services:
openpanel:
build: .
ports:
- "3001:3001"
volumes:
- openpanel-data:/data
- C:/Users/you/manga:/libraries/manga:ro
restart: unless-stopped
volumes:
openpanel-data:docker compose up -d
You'll need Rust 1.75+ (via rustup), Node.js 20+ (or Bun), and the Visual Studio C++ Build Tools.
cd ui npm install npm run build
cd server cargo build --release
Set environment variables and start the server:
$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.
Use NSSM to register OpenPanel as a Windows service:
winget install nssm
Register and configure the service:
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:
nssm start OpenPanel
Manage with nssm stop OpenPanel, nssm restart OpenPanel, or through the Windows Services panel (services.msc).
Configure via environment variables or a .env file placed in the server/ directory:
| Variable | Default | Description |
|---|---|---|
| OPENPANEL_PORT | 3001 | HTTP server port |
| OPENPANEL_DATA_DIR | ./data | SQLite database + thumbnail storage |
| DATABASE_URL | sqlite://<DATA_DIR>/openpanel.db | SQLite connection URL |
| OPENPANEL_LIBRARY_ROOTS | (empty) | Comma-separated library paths to scan on startup |
| OPENPANEL_DEV_MODE | false | Enable CORS for localhost dev servers |
| OPENPANEL_LOG_LEVEL | info | Tracing log level (debug, info, warn, error) |
| OPENPANEL_ZIP_CACHE_SIZE | 200 | ZIP index LRU cache size |
| OPENPANEL_PUBLIC_URL | http://localhost:3001 | Public URL (used for CORS) |
| OPENPANEL_SCAN_ON_STARTUP | true | Auto-scan libraries on server start |
Place this file in server/.env. The server loads it automatically on start:
OPENPANEL_PORT=3001 OPENPANEL_DATA_DIR=./data OPENPANEL_DEV_MODE=true OPENPANEL_LIBRARY_ROOTS=/home/user/manga,/home/user/comics OPENPANEL_LOG_LEVEL=info
/libraries/manga), not the host path.docker compose pull docker compose up -d
Pull the latest code, rebuild both frontend and backend, then restart:
git pull cd ui && npm ci && npm run build && cd .. cd server && cargo build --release && cd .. sudo systemctl restart openpanel
git pull cd ui; npm ci; npm run build; cd .. cd server; cargo build --release; cd .. nssm restart OpenPanel
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.
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.
navigator.storage.persist() to protect your downloads from browser eviction.All routes are under /api/. Auth routes are public; most others require a Bearer token in the Authorization header.
| Method | Path | Description |
|---|---|---|
| GET | /api/health | Health check |
| POST | /api/auth/register | Register a new user |
| POST | /api/auth/login | Login (returns token) |
| POST | /api/auth/logout | Logout (invalidates token) |
| GET | /api/auth/me | Current user info |
| GET | /api/auth/status | Setup status |
| GET | /api/libraries | List all libraries |
| GET | /api/series | List series (sort, genre, status) |
| GET | /api/series/:id/books | Books in a series |
| GET | /api/series/:id/chapters | Detected chapters |
| GET | /api/series/:id/metadata | AniList metadata |
| GET | /api/genres | List all genres |
| GET | /api/books/:id | Book details |
| GET | /api/books/:id/pages/:num | Stream a page image |
| GET | /api/books/:id/thumbnail | Book thumbnail (WebP) |
| GET | /api/series/:id/thumbnail | Series thumbnail (WebP) |
| GET/PUT | /api/progress | Reading progress |
| GET | /api/continue-reading | Continue reading list |
| GET/POST | /api/bookmarks | List/create bookmarks |
| DELETE | /api/bookmarks/:id | Delete a bookmark |
| GET/POST | /api/collections | List/create collections |
| POST | /api/collections/:id/items | Add series to collection |
| GET/PUT | /api/preferences | User preferences |
| GET | /api/version | Server version |
| POST | /api/admin/scan | Trigger library scan |
| POST | /api/admin/libraries | Add a library |
| GET | /api/admin/profiles | List users |
| PUT | /api/admin/profiles/:id/reset-password | Reset user password (admin) |
| POST | /api/admin/backup | Create backup |
| POST | /api/admin/update | Trigger server update |
OpenPanel has three main components:
Frontend
React SPA
Vite PWA
Backend
Axum (Rust)
REST API
Database
SQLite
WAL mode
Stop and remove the containers, then optionally delete the data volume:
docker compose down
This stops the containers but keeps your data volume. To also delete the database and thumbnails:
docker compose down -v
To remove the Docker image as well:
docker rmi openpanel
Stop and disable the service, then remove the files:
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:
sudo rm -rf /opt/openpanel sudo rm -rf /var/lib/openpanel
If running as a service via NSSM:
nssm stop OpenPanel nssm remove OpenPanel confirm
Then delete the application and data folders:
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.
If you've forgotten the admin password, you have several options depending on your access level.
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.
Any logged-in user can change their password from the Profile page. Enter your current password, then set a new one.
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.
sqlite3 data/openpanel.db "DELETE FROM profiles WHERE role = 'admin';"
Restart the server and create a new admin account in the browser.
If you want to keep the admin user's progress and bookmarks, generate a new bcrypt password hash and update the record:
htpasswd -nbBC 12 "" "newpassword" | tr -d ':\n' | sed 's/$2y/$2b/'
Then update the database with the generated hash:
sqlite3 data/openpanel.db "UPDATE profiles SET password_hash = '<paste_hash>' WHERE username = 'admin';"
Restart the server for changes to take effect.
OpenPanel provides multiple ways to manage passwords depending on your role.
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.
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.
If you already have nginx, Traefik, or another reverse proxy, you can route traffic to OpenPanel's port. Here's an example nginx config:
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.
If port 3001 is taken, change the port via the OPENPANEL_PORT environment variable:
OPENPANEL_PORT=8080 ./target/release/openpanel-server
For Docker, update the port mapping in docker-compose.yml:
ports: - "8080:3001"
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.
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.
If the server fails to start with SQLite errors, try running an integrity check:
sqlite3 data/openpanel.db "PRAGMA integrity_check;"
If corruption is found, you can attempt a recovery by exporting and reimporting:
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.
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.
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.
The ZIP index cache defaults to 200 entries. If you have a very large library and limited RAM, lower it:
OPENPANEL_ZIP_CACHE_SIZE=50