跳至主要内容

⬇️⬇️⬇️ 欢迎关注我的 telegram 频道和 twitter ⬇️⬇️⬇️


联系方式: Twitter Github Email Telegram

Building a Personal Manga Library with Komga

September 3, 2026 1358 字 阅读约 7 分钟

Introduction

I recently set up my own manga library. I download the manga, put it into Komga running on Kubernetes, and read it on my iPhone with KMReader. The files and reading progress stay under my control, and the whole setup is easier to use than I expected.

How I use it

The workflow is pretty simple:

Download manga from a manga website
Upload it to Komga's /data directory
Let Komga scan and organize it
Read it on an iPhone with KMReader

Komga handles the server side. It scans local directories, sorts manga into series and books, and manages covers, metadata, and reading progress.

KMReader is a native Komga client for iOS. It can stream books or download the whole book for offline reading. Reading progress syncs back to Komga automatically. I use the version from the Chinese App Store:

KMReader - App Store

I will not go into where the manga files come from. Pay attention to copyright and try to organize only content you have the right to access.

Why I use Komga

At first, I just wanted a manga reader for iOS. Then I realized that keeping files only on my phone was awkward. Switching devices, clearing the cache, and syncing reading progress were all annoying.

Komga moves those jobs to the server. It supports cbz, zip, cbr, rar, pdf, and epub. It also has a browser reader, though I normally use KMReader.

It also fits my existing self-hosted setup. The service runs in K8s, Traefik exposes it over HTTPS, and ArgoCD manages the configuration. It works the same way as my other services, so I do not need to maintain a separate setup just for it.

K8s deployment

The official Komga Docker documentation has a few important details:

  • The service listens on port 25600
  • /config stores the database and configuration
  • /data stores the manga files
  • The container can run as 1000:1000
  • /config should use a local filesystem, not CIFS or NFS

I use a StatefulSet with one replica. There is only one Volume, split into config and data directories with subPath.

Here is the main configuration:

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: komga
  namespace: app
spec:
  serviceName: komga
  replicas: 1
  podManagementPolicy: OrderedReady
  updateStrategy:
    type: RollingUpdate
  selector:
    matchLabels:
      app: komga
  template:
    metadata:
      labels:
        app: komga
    spec:
      securityContext:
        fsGroup: 1000
        fsGroupChangePolicy: OnRootMismatch
      initContainers:
      - name: init-storage
        image: busybox:1.37.0
        command:
        - sh
        - -c
        - mkdir -p /storage/config /storage/data && chown -R 1000:1000 /storage/config /storage/data
        securityContext:
          runAsUser: 0
          runAsGroup: 0
          allowPrivilegeEscalation: false
        volumeMounts:
        - name: storage
          mountPath: /storage
      containers:
      - name: komga
        image: gotson/komga:1.26.3
        imagePullPolicy: IfNotPresent
        securityContext:
          runAsUser: 1000
          runAsGroup: 1000
          runAsNonRoot: true
          allowPrivilegeEscalation: false
        ports:
        - name: http
          containerPort: 25600
        env:
        - name: TZ
          value: Asia/Shanghai
        - name: JAVA_TOOL_OPTIONS
          value: -Xmx1536m
        volumeMounts:
        - name: storage
          mountPath: /config
          subPath: config
        - name: storage
          mountPath: /data
          subPath: data
  volumeClaimTemplates:
  - metadata:
      name: storage
    spec:
      accessModes:
      - ReadWriteOnce
      storageClassName: local-path
      resources:
        requests:
          storage: 100Gi

The StatefulSet creates a PVC named storage-komga-0. /config and /data remain separate directories inside the container, but they share one underlying Volume. Backups are simpler too, since I can back up the whole Volume together.

The initContainer creates the directories and fixes their permissions. Komga itself still runs as 1000:1000, so the main container does not need root access.

The Service is a regular ClusterIP:

apiVersion: v1
kind: Service
metadata:
  name: komga
  namespace: app
spec:
  type: ClusterIP
  selector:
    app: komga
  ports:
  - name: http
    port: 80
    targetPort: http

Traefik handles external access. The address looks like this:

https://komga.example.com

Creating the manga library

Once Komga is running, open the web interface and create the administrator account.

Click the + next to Libraries in the sidebar and create a manga library. Set the Root Folder to:

/data

Komga treats each subdirectory under the library as a series, so it is better to organize the folders first:

/data/
├── One Piece/
│   ├── One Piece Volume 001.epub
│   ├── One Piece Volume 002.epub
│   └── One Piece Volume 003.epub
├── Naruto/
│   ├── Naruto Volume 001.cbz
│   └── Naruto Volume 002.cbz
└── One-Punch Man/
    └── One-Punch Man Volume 001.cbz

Do not dump every file directly into the /data root. It may look fine at first, but it gets messy once the library grows.

Uploading manga

Komga’s web interface is not a cloud drive. It does not have a regular button for uploading local files to the server. It scans directories that already exist on the server.

I currently use kubectl cp. First, create a directory for the series:

kubectl exec -n app komga-0 -- mkdir -p "/data/One Piece"

Then copy the local file into it:

kubectl cp "/path/to/One Piece Volume 001.epub" \
  app/komga-0:"/data/One Piece/One Piece Volume 001.epub"

After the upload, run Scan Library Files in Komga. It finds the new file, analyzes its format and page count, and generates a cover.

For larger batches, you can keep a separate import directory:

/data/import
/data/library

Set the library root to /data/library and upload files to /data/import first. In Import > Books, scan that directory, choose the destination series, then move, copy, or hard-link the files into place. Both directories are on the same Volume, so hard links can work here.

I do not bother with the extra step for now. I upload each book straight into its series directory under /data, then run a scan.

Connecting KMReader

Open KMReader on the iPhone and tap + to add a server.

Enter the server address:

https://komga.example.com

Use the root address. There is no need to add /opds. You can sign in with a Komga username and password or an API key. Once connected, the libraries, covers, and reading progress sync to the app.

KMReader has three offline policies for each series:

  • Manual: download books manually
  • Latest: automatically keep the latest books downloaded
  • All: download the entire series

I prefer Manual. I cache only the books I plan to read, which saves space on the phone. They still work without a connection, and KMReader syncs the progress back to Komga when the phone is online again.

Problems I ran into

Problem 1. There is no regular upload button in Komga

I spent a while looking for one in the web interface. Komga does not accept browser uploads like a cloud drive. It scans files that are already in a server directory. In K8s, the easiest option is kubectl cp, or you can mount a separate download directory.

Problem 2. Each series needs its own directory

Komga organizes series based on parent directories. If the folders are messy, fixing them later takes more work. I rename the directories and files after downloading them instead of cleaning everything up in the admin interface.

Problem 3. Do not put /config on network storage

The official documentation specifically says that /config must use a local filesystem. It contains Komga’s database and configuration. I use local-path and keep it on the same local Volume as the manga data.

Problem 4. One Volume needs subPath to separate the directories

I did not want to maintain two PVCs, so I created one called storage. It is mounted at /config and /data inside the container. Without subPath, both mount points would show the same root directory and the directory layout would get mixed together.

Problem 5. KMReader needs the server root address

KMReader talks directly to the Komga API. It is not a generic OPDS reader. Use an address such as https://komga.example.com. Adding the OPDS path prevents it from connecting to the regular Komga API.

How it feels to use

Now I download a manga, put it under /data/manga-name/, and run a scan in Komga. It then appears in KMReader on my iPhone. If I want to read offline, I download it first. I do not have to keep track of my reading progress myself either.

There is no complicated architecture here. One StatefulSet, one Volume, and one domain name. Since I already run K8s and ArgoCD, the maintenance cost is basically negligible.

I may automate the download, directory cleanup, and library scan later. For now, manual uploads are good enough. I would rather read the manga first.

Feel free to follow my blog at www.bboy.app

Have Fun