Deployment guide

Codex Comics

Overview

Codex is an open source, self-hosted comic archive server and reader. Point it at folders of comic and ebook archives you already own, and it indexes them into a browsable library with covers, metadata, full-text search and a browser-based reader.

This SecureAnyCloud image runs Codex 2.2.6 on a hardened Ubuntu 24.04 LTS base, patched at build time. It ships with no working credential. Each VM generates its own administrator password and application secret keys on first boot, and Codex is published only after that finishes.

Codex is a library manager for files you supply. It does not search for, download or acquire content, and nothing of that kind is present in this image.

What you get:

  • Codex 2.2.6 from PyPI in a dedicated Python 3.12 virtual environment, managed by systemd and served by the Granian application server on loopback only
  • Support for CBZ, CBR, CB7, CBT and PDF, with embedded ComicInfo and comicbox metadata, folder watching and an OPDS feed so e-readers and reader apps can pull from the same library
  • nginx on :80 reverse proxying Codex, including the WebSocket Codex uses for live library updates
  • A dedicated 64 GiB data disk mounted at /var/lib/codex, holding the database, the covers cache and your comic library
  • A per-VM administrator password, Django secret key and field encryption key, generated on first boot and recorded in a root-only file
  • Anonymous browsing and self-registration both disabled, so a fresh VM is private until you decide otherwise
  • A six-page SecureAnyCloud sample comic, so the library and reader work the moment you sign in
  • Nothing bound to port 80 until first boot has provisioned and secured the instance. Codex documents a default administrator of admin with the password admin, so that account ships with an unusable password
  • 24/7 SecureAnyCloud support

Key facts:

  • Default SSH user: azureuser
  • Web interface: http://<vm-public-ip>/
  • OPDS feed: http://<vm-public-ip>/opds
  • Application port: 127.0.0.1:9810 (loopback only, reached through nginx)
  • Credentials file: /root/codex-credentials.txt (mode 600, root only)
  • Data disk: /var/lib/codex (config, database, covers cache, library)
  • Recommended size: Standard_B2s

Prerequisites

You need an active Azure subscription, an SSH key pair, and a VNet with a subnet in the target region.

Standard_B2s (2 vCPU / 4 GiB RAM) is a sensible starting point for a personal or small-team library. Choose a larger size for very large collections, because the first scan of a big library is CPU- and IO-heavy.

In the network security group (NSG), allow inbound 22/tcp from your management network and 80/tcp so your browser and reader apps can reach Codex. Add 443/tcp once you terminate TLS on the VM.

Step 1 - Deploy from the Azure Marketplace

Sign in to the Azure Portal, choose Create a resource, search the Marketplace for Codex by SecureAnyCloud, and select Create.

On the Basics tab, pick your subscription, resource group, region and size. Under Administrator account, choose SSH public key and paste your key. Under Inbound port rules, allow SSH (22) and HTTP (80). Then select Review + create and Create.

The image carries its own 64 GiB data disk. It is attached and mounted automatically at /var/lib/codex, so you do not need to add or format anything.

Step 2 - Deploy from the Azure CLI

# Accept the Marketplace terms once per subscription (required before the first CLI deploy)
az vm image terms accept --urn sac1647283583153:codex-comics-ubuntu-24-04:default:latest
az vm create \
  --resource-group <your-rg> \
  --name codex \
  --image sac1647283583153:codex-comics-ubuntu-24-04:default:latest \
  --size Standard_B2s \
  --admin-username azureuser \
  --generate-ssh-keys \
  --public-ip-sku Standard

Open port 80 so your browser and reader apps can reach Codex:

az vm open-port --resource-group <your-rg> --name codex --port 80

Step 3 - Confirm the services are running

SSH in as azureuser. First boot takes well under a minute. When it finishes, nginx binds port 80 and Codex is ready.

systemctl is-active codex nginx codex-firstboot

Check the versions and confirm that Codex answers its health endpoint through nginx:

/opt/codex/venv/bin/python -c "import importlib.metadata as m; print('Codex', m.version('codex'))"; nginx -v 2>&1; curl -s -o /dev/null -w 'GET /health -> HTTP %{http_code}\n' http://127.0.0.1/health

If port 80 is not bound, first boot has not completed. This is deliberate: the image fails closed, and Codex is never published before it has been provisioned and secured. Check progress with systemctl status codex-firstboot.service and journalctl -u codex-firstboot.service.

Step 4 - Retrieve this VM's administrator credentials

Every VM generates its own administrator password on first boot. The password and the sign-in address are written to a root-only credentials file. Display it with:

sudo cat /root/codex-credentials.txt

First boot also regenerated this VM's Django secret key and field encryption key. You never need those values. You can confirm that they are unique to this machine, without printing them, and check the file permissions:

stat -c '%a %U:%G %n' /root/codex-credentials.txt /var/lib/codex/config/secret_key /var/lib/codex/config/field_encryption_key

You can prove from the command line that the generated password authenticates. The bundled probe signs in with the password from the credentials file and retries with a deliberately wrong one. It checks that an anonymous caller is refused, then reads an actual comic page image from the reader API:

sudo /usr/local/sbin/codex-cred-roundtrip.sh

Confirm that nothing is readable without signing in, and that Codex listens only on loopback:

ss -tlnH | awk '{print $1, $4}' | sort -u; for p in /api/v4/auth/profile /opds/v1.2/ /api/v4/comics/1/pages/0; do printf '%-30s anonymous -> HTTP ' "$p"; curl -s -o /dev/null -w '%{http_code}\n' "http://127.0.0.1$p"; done

Step 5 - Sign in to Codex

Browse to http://<vm-public-ip>/. Anonymous browsing is switched off in this image, so Codex opens on an Unauthorized screen with a Login button instead of exposing your library to the internet.

Select Login and enter admin with the codex.admin.pass value from Step 4.

After your first sign-in, change the password. Open the hamburger menu, then Admin Panel, then the Users tab.

Step 6 - Browse the library

After you sign in, Codex opens the browser view. A SecureAnyCloud sample comic is already imported, so you can see covers, metadata and the reader working before you add anything of your own.

Use top collection to switch between publishers, imprints, series, volumes, issues and folders. Use filter by to narrow by reading status or metadata. Use the magnifier for full-text search across your library.

Step 7 - Read a comic

Select a cover to open the reader. Click or tap the middle of the page to show the toolbars, the page slider and the reading controls. Turn pages with the arrows or your keyboard. Codex remembers where you were.

Reading settings are in the hamburger menu inside the reader. You can fit pages to width, height or screen, choose one page or two-page spreads, and set a left-to-right or right-to-left reading direction for manga.

Step 8 - Add your own comic library

Your own comics go on the dedicated data disk, where the pre-registered library already points. Copy your archives from your workstation with any file transfer tool, for example scp -r ./my-comics azureuser@<vm-ip>:/tmp/. Then move them into place and hand ownership to the service account:

sudo install -d -o codex -g codex -m 0755 /var/lib/codex/library/My\ Comics;
sudo chown -R codex:codex /var/lib/codex/library; ls -ld /var/lib/codex/library/My\ Comics

Codex watches the library folder for file events and also polls it hourly, so anything you add is picked up automatically. To index immediately, open the hamburger menu and choose Poll All Libraries. To add another folder, open Admin Panel, then the Libraries tab, then select + Library.

The Admin Panel also holds Users and Groups for granting people access, Auth for optional single sign-on, Settings for library defaults and rate limits, and Restore for the user data sidecar described in Step 10.

To delete the bundled sample, remove the SecureAnyCloud Samples folder from /var/lib/codex/library and poll the library again.

Step 9 - Read on an e-reader with OPDS

Codex publishes a standard OPDS catalogue. Any OPDS-capable reader app can browse and download from your library with the same credentials:

printf 'anonymous OPDS: HTTP %s\n' "$(curl -s -o /dev/null -w '%{http_code}' http://127.0.0.1/opds/v1.2/)"; printf 'authenticated OPDS: HTTP %s\n' "$(curl -s -o /dev/null -w '%{http_code}' -u "admin:<CODEX_ADMIN_PASSWORD>" http://127.0.0.1/opds/v1.2/)"

A 401 for the anonymous request and a 200 for the authenticated one is the expected result. Point your reader app at http://<vm-public-ip>/opds and sign in with admin and this VM's password.

Step 10 - Storage, persistence and backups

Everything that matters lives on the dedicated data disk, separate from the operating system disk. You can resize or snapshot it independently:

findmnt -no SOURCE,TARGET,FSTYPE,OPTIONS /var/lib/codex; df -h /var/lib/codex | tail -1; ls -l /var/lib/codex/config/codex.sqlite3

Path Holds

/var/lib/codex/config codex.sqlite3, codex.toml, secret keys, covers cache, logs

/var/lib/codex/library Your comic archives

/opt/codex/venv The Codex Python virtual environment

/etc/codex/codex.env Service environment: config directory and the loopback bind

/etc/nginx/sites-available/codex The public reverse proxy vhost

Codex writes a user_data.sqlite sidecar next to its database. It holds what a library rescan cannot rebuild: accounts, groups, permissions, bookmarks, reading progress and preferences.

Snapshot the data disk for a full backup. Keep a copy of the sidecar if you ever plan to rebuild the library from scratch. The Restore tab in the Admin Panel replays it.

Step 11 - Change the service configuration

The ports and bind address come from /etc/codex/codex.env, which takes precedence over codex.toml:

cat /etc/codex/codex.env

Codex is bound to 127.0.0.1 on purpose, so nginx is the only public entry point. If you change that binding, add a matching NSG rule. Put TLS in front rather than exposing the application port directly. After any change, restart the service:

sudo systemctl restart codex.service && sleep 8 && systemctl is-active codex nginx

Step 12 - Enable HTTPS

Codex authenticates over whatever transport you give it, so put TLS in front before you expose an instance to the internet. Point a DNS record at the VM and open 443/tcp in the NSG. Then install a certificate with certbot for nginx:

sudo apt-get update && sudo apt-get install -y certbot python3-certbot-nginx
sudo certbot --nginx -d your-domain.example -m you@your-domain.example --agree-tos --redirect -n

certbot edits the shipped vhost in place and adds the redirect. The proxy already forwards the real scheme to Codex, so sessions and CSRF protection keep working over HTTPS with no further changes.

Step 13 - Keep the VM patched

Unattended upgrades are enabled, so the base OS installs security updates on its own. To apply all updates immediately, run:

sudo apt-get update && sudo apt-get -y dist-upgrade && sudo reboot

To move to a newer Codex release, upgrade inside the virtual environment and restart the service:

sudo /opt/codex/venv/bin/pip install --upgrade codex
sudo systemctl restart codex.service

Security notes

  • No credential ships in the image. The built-in admin account has an unusable password, so the upstream documented admin / admin default never exists on a SecureAnyCloud VM. First boot sets a random password for this VM only.
  • Nothing listens on port 80 until first boot completes. The public vhost is staged but not enabled in the image. The first-boot service enables it only after the administrator password is set and Codex answers on loopback.
  • The application port is never exposed. Codex binds 127.0.0.1:9810, and only 22 and 80 are reachable from the network.
  • Anonymous browsing and self-registration are switched off. A fresh VM is private. To allow a public library or open sign-ups, turn either back on under Admin Panel, then Settings.
  • Per-VM cryptographic material. The Django secret key and field encryption key are regenerated on every VM's first boot, so no session token or encrypted field is portable between instances.
  • Restrict access. Limit 22/tcp to your management network, put TLS in front before exposing the instance, and create individual accounts in the Admin Panel instead of sharing the administrator login.

Architecture summary

Component Detail

Codex 2.2.6 from PyPI, Python 3.12 virtual environment at /opt/codex/venv

Application server Granian, bound to 127.0.0.1:9810

Reverse proxy nginx on :80, HTTP plus the /api/v4/ws WebSocket

Database SQLite at /var/lib/codex/config/codex.sqlite3

Archive support CBZ, CB7, CBT and PDF natively, CBR and RAR through unar

Services codex.service, nginx.service, codex-firstboot.service (one shot)

Base image Ubuntu 24.04 LTS, fully patched at capture

Support

Codex is licensed under the GNU General Public License version 3. The licence text and the SecureAnyCloud attribution notice ship in the image at /usr/share/doc/sac/. SecureAnyCloud packages unmodified upstream Codex, and the additions are packaging only.

SecureAnyCloud provides 24/7 support for this image at sales@secureanycloud.com. For questions about Codex itself, see the Codex documentation and the upstream project.

Deploy on Azure

Launch Codex on Ubuntu 24.04 LTS with 24/7 support from SecureAnyCloud.

View this image

Need Help?

Our support team is available 24/7.

sales@secureanycloud.com

Deploy on Azure