Skip to content

Media & Archives

📚 Things I Want To Keep

I grouped these together because they all deal with keeping stuff around: books, comics, downloads, web pages, and other bits I do not want to lose track of.

They are a good reminder that some services are mostly about storage layout, library paths, and persistence rather than raw horsepower.

🗃️ ArchiveBox

ArchiveBox Logo

Web Archive

ArchiveBox is what I use when a bookmark feels too optimistic. I like having a stack that can actually save the thing, not just point at where it used to live.

Config

This is a multi-container stack: the main app, Sonic for search, noVNC for browser access, and PyWB for WARC playback. It is a nice example of one product getting real value from helper services instead of pretending everything fits cleanly in one container.

Compose

name: archivebox

networks:
  archivebox:
    name: archivebox

services:
  archivebox:
    image: archivebox/archivebox:${ARCHIVEBOX_VERSION:-latest}
    networks:
      - archivebox
    ports:
      - ${ARCHIVEBOX_PORT:-8000}:8000
    volumes:
      - ${ARCHIVEBOX_DATA_PATH}:/data
    environment:
      - LISTEN_HOST=0.0.0.0:8000
      - ALLOWED_HOSTS=*
      - CSRF_TRUSTED_ORIGINS=https://${ARCHIVEBOX_URL},http://${ARCHIVEBOX_URL}
      - PUBLIC_INDEX=False
      - PUBLIC_SNAPSHOTS=False
      - PUBLIC_ADD_VIEW=False
      - SEARCH_BACKEND_ENGINE=sonic
      - SEARCH_BACKEND_HOST_NAME=sonic
      - SEARCH_BACKEND_PASSWORD=${ARCHIVEBOX_SEARCHBACKEND_PASSWORD}
      - YTDLP_MAX_SIZE=1024m
      - TIMEOUT=120
      - CHECK_SSL_VALIDITY=True
      - SAVE_ARCHIVEDOTORG=False
    restart: unless-stopped

  sonic:
    image: archivebox/sonic:${ARCHIVEBOX_SONIC_VERSION:-latest}
    networks:
      - archivebox
    expose:
      - 1491
    environment:
      - SEARCH_BACKEND_PASSWORD=${ARCHIVEBOX_SEARCHBACKEND_PASSWORD}
    volumes:
      - ${ARCHIVEBOX_DATA_PATH}/sonic:/var/lib/sonic/store
    restart: unless-stopped

  novnc:
    image: theasp/novnc:${ARCHIVEBOX_NOVNC_VERSION:-latest}
    networks:
      - archivebox
    environment:
      - DISPLAY_WIDTH=1920
      - DISPLAY_HEIGHT=1080
      - RUN_XTERM=no
    ports:
      - ${ARCHIVEBOX_NOVNC_PORT:-8080}:8080
    restart: unless-stopped

  pywb:
    image: webrecorder/pywb:${ARCHIVEBOX_PYWB_VERSION:-latest}
    entrypoint: /bin/sh -c '(wb-manager init default || test $$? -eq 2) && wb-manager add default /archivebox/archive/*/warc/*.warc.gz; wayback;'
    environment:
      - INIT_COLLECTION=archivebox
    ports:
      - ${ARCHIVEBOX_PYWB_PORT:-8686}:8080
    volumes:
      - ${ARCHIVEBOX_DATA_PATH}:/archivebox
      - ${ARCHIVEBOX_DATA_PATH}/wayback:/webarchive
    restart: unless-stopped

Example env

ARCHIVEBOX_VERSION=latest
ARCHIVEBOX_SONIC_VERSION=latest
ARCHIVEBOX_NOVNC_VERSION=latest
ARCHIVEBOX_PYWB_VERSION=latest
ARCHIVEBOX_DATA_PATH="/srv/archivebox"
ARCHIVEBOX_PORT=8000
ARCHIVEBOX_URL=archivebox.example.com
ARCHIVEBOX_SEARCHBACKEND_PASSWORD=change-me
ARCHIVEBOX_NOVNC_PORT=8080
ARCHIVEBOX_PYWB_PORT=8686

Vars

Variable Purpose Why it matters
ARCHIVEBOX_VERSION Main image tag Pin if you want steady upgrades
ARCHIVEBOX_DATA_PATH Data directory All archives and helper data live here
ARCHIVEBOX_PORT Main web UI port App access
ARCHIVEBOX_URL Public hostname Needed for trusted origins
ARCHIVEBOX_SEARCHBACKEND_PASSWORD Sonic password Protects the search backend connection
ARCHIVEBOX_NOVNC_PORT noVNC port Remote browser access
ARCHIVEBOX_PYWB_PORT Playback port Exposes the replay interface

📚 Komga

Komga Logo

Comic Library

Komga is the one I actually use now. I like it because it shows what is really in my files instead of depending too much on smart matching, which makes it a lot more reliable for niche manga and odd library entries that metadata services do not recognize properly.

Config

The structure is similar to Kavita: one config volume, one library mount, one web port, plus a small app flag to allow OAuth-backed account creation.

Compose

name: komga

volumes:
  komga:
    name: ${KOMGA_VOLUME:-komga}

services:
  komga:
    image: gotson/komga:${KOMGA_VERSION:-latest}
    container_name: komga
    volumes:
      - komga:/config
      - ${LIBRARY_PATH:?LIBRARY_PATH;No library path specified}:/mnt/library:z
    environment:
      TZ: ${TZ:-Etc/UTC}
      KOMGA_OAUTH2_ACCOUNT_CREATION: true
    ports:
      - ${KOMGA_PORT:-25600}:25600
    restart: unless-stopped

Example env

LIBRARY_PATH="/srv/library"
TZ="Asia/Tokyo"
KOMGA_VERSION=latest
KOMGA_PORT=25600
KOMGA_VOLUME=komga

Vars

Variable Purpose Why it matters
KOMGA_VERSION Image tag App version selection
KOMGA_PORT Host port Web UI access
KOMGA_VOLUME Config volume Keeps settings and metadata
LIBRARY_PATH Host library path Source of the collection
TZ Timezone Operational consistency

📖 Kavita

Kavita Logo

Smart Matching

Kavita is still the easier one to manage when its metadata matching works well. It can make a library feel cleaner with less manual effort, especially for books and more common manga, but I ran into enough niche titles not being recognized that I ended up sticking with Komga for daily use.

Config

This is a clean single-service recipe: one config volume, one bind mount for the real library, one web port, and a timezone setting for cleaner logs.

Compose

name: kavita

volumes:
  kavita:
    name: ${KAVITA_VOLUME:-kavita}

services:
  kavita:
    image: jvmilazz0/kavita:${KAVITA_VERSION:-latest}
    container_name: kavita
    volumes:
      - kavita:/kavita/config
      - ${LIBRARY_PATH:?LIBRARY_PATH;No library path specified}:/mnt/library:z
    environment:
      TZ: ${TZ:-Etc/UTC}
    ports:
      - ${KAVITA_PORT:-5000}:5000
    restart: unless-stopped

Example env

LIBRARY_PATH="/srv/library"
TZ="Asia/Tokyo"
KAVITA_VERSION=latest
KAVITA_PORT=5000
KAVITA_VOLUME=kavita

Vars

Variable Purpose Why it matters
KAVITA_VERSION Image tag Version control
KAVITA_PORT Host port Web UI access
KAVITA_VOLUME Config volume Keeps app state between restarts
LIBRARY_PATH Host library path The actual content source
TZ Timezone Useful for logs and schedules

⬇️ JDownloader 2 And qBittorrent

JDownloader Logo

Download Pair

I keep JDownloader 2 and qBittorrent together because between the two of them, they cover almost all of my personal download needs. They also show a setup pattern I reuse a lot: shared content path, separate app state, and explicit user mapping so permissions stay sane.

For JDownloader, I use the popular Docker image by jlesage because it is well-known, easy to run, and just works without much drama.

Config

Both containers use the same downloads directory from the host but keep separate named volumes for their own configuration. That split is exactly what I want: shared content, separate app state.

Compose

name: downloader

volumes:
  jdownloader:
    name: ${JDOWNLOADER_VOLUME:-jdownloader}
  qbittorrent:
    name: ${QBITTORENT_VOLUME:-qbittorrent}

services:
  jdownloader:
    container_name: jdownloader
    image: jlesage/jdownloader-2:${JDOWNLOADER_VERSION:-latest}
    ports:
      - ${JDOWNLOADER_PORT:-5800}:5800
    volumes:
      - jdownloader:/config
      - ${DOWNLOADS_PATH:?DOWNLOADS_PATH;No download path specified}:/output:z
    environment:
      TZ: ${TZ:-Etc/UTC}
      UMASK: 0000
      PUID: ${UID:-1000}
      PGID: ${GID:-1000}
      KEEP_APP_RUNNING: 1
      DARK_MODE: 1
    restart: unless-stopped

  qbittorrent:
    image: lscr.io/linuxserver/qbittorrent:${QBITTORENT_VERSION:-latest}
    container_name: qbittorrent
    volumes:
      - qbittorrent:/config
      - ${DOWNLOADS_PATH:?DOWNLOADS_PATH;No download path specified}:/downloads:z
    environment:
      TZ: ${TZ:-Etc/UTC}
      UMASK: 0000
      PUID: ${UID:-1000}
      PGID: ${GID:-1000}
    ports:
      - ${QBITTORENT_PORT:-8080}:8080
      - 6881:6881
      - 6881:6881/udp
    restart: unless-stopped

Example env

UID=1000
GID=1000
DOWNLOADS_PATH="/srv/downloads"
TZ="Asia/Tokyo"
JDOWNLOADER_VERSION=latest
JDOWNLOADER_PORT=5800
JDOWNLOADER_VOLUME=jdownloader
QBITTORENT_VERSION=latest
QBITTORENT_PORT=8080
QBITTORENT_VOLUME=qbittorrent

Vars

Variable Purpose Why it matters
DOWNLOADS_PATH Shared download directory Main place where files land
UID / GID Host user and group IDs Prevents permission surprises
TZ Timezone Keeps logs and schedules sane
JDOWNLOADER_VERSION / QBITTORENT_VERSION Image tags Version control for each app
JDOWNLOADER_PORT / QBITTORENT_PORT Web ports Access to both interfaces
JDOWNLOADER_VOLUME / QBITTORENT_VOLUME Config volumes Preserve app state

Comments