# Introduction (/en/docs) ImgYoung is a self-hosted image host with multi-channel failover. Every image is stored on several storage channels, so losing one does not break uploads or links. ## What ImgYoung is [#what-imgyoung-is] ImgYoung is a self-hosted image host. It runs as a single Go binary (or a single Docker image), ships with a web admin console, and is MIT licensed. Storage is built on [rclone](https://rclone.org/), so images can live on any backend rclone supports: S3 / R2 / MinIO and other object stores, WebDAV, consumer cloud drives, or the local disk. Each such storage configuration is called a **channel** in ImgYoung. ## The problem it solves [#the-problem-it-solves] The real risk of running your own image host is not missing features, it is losing the images: a free image host shuts down, an object storage account gets suspended, a cloud drive throttles you, or the server's disk dies, and every image link in your blog and notes breaks at once. ImgYoung is designed to keep image links working for the long run: - **Every image is stored more than once.** An upload returns as soon as the primary channel has it; the other channels receive replicas asynchronously. Pending replication survives restarts. - **Reads fail over automatically.** By default ImgYoung proxies image responses itself; if one channel cannot serve the bytes, it reads from the next channel that has a replica, and the link stays the same. - **The ledger is backed up too.** The metadata database that records which image lives on which channel is snapshotted on a schedule and written to every healthy channel, so losing the host does not take that mapping with it. ## Core capabilities [#core-capabilities] | Capability | What it does | |---|---| | Multi-channel failover | The healthy channel with the highest priority is the primary channel. Periodic health checks switch away from a failing channel automatically; replicas are filled in asynchronously and replication progress is visible in the console. See [Storage channels](./channels/index.mdx) | | Channel encryption | Built on rclone crypt: both content and file names are encrypted, so the storage provider only sees ciphertext. With the master key you can decrypt the data yourself using stock rclone. See [Channel encryption](./channels/encryption.mdx) | | Automatic metadata backups | By default a snapshot is taken every 24 hours and written to every healthy channel, each keeping its own latest 7. The console can back up, download and restore on demand. See [Backup and restore](./operations/backup.mdx) | | TTL lifecycle and cold-image review | Expiry is resolved as per-upload > per-user default > global default, and access-based renewal is on by default. Images with no public access for 90 days (default) land in the cleanup list, where an admin decides to delete or keep them; nothing is deleted automatically. See [Lifecycle](./guides/lifecycle.mdx) | | Background deduplication | Byte-identical images are merged into one physical file automatically; near-duplicates (by perceptual hash) go to a review list. Deduplication runs in the background and never slows down uploads. See [Admin console](./guides/admin.mdx) | | On-demand processing | Append `?w=400` to resize to a preset width; browsers that accept WebP get WebP automatically. Results are cached on local disk. See [Access and links](./guides/access.mdx) | | Multiple users and quotas | Admins create accounts; each user has their own images, upload tokens and storage quota, and regular users cannot see each other's data. See [Admin console](./guides/admin.mdx) | | Many ways to upload | Drag-and-drop or paste in the console, the HTTP upload API, fetch from URL, the S3 protocol (PicList's built-in AWS S3 uploader works as is; PicGo works once its S3 plugin is installed), and PicGo / PicList custom uploaders. See [Uploading](./guides/upload.mdx) and [Clients](./guides/clients.mdx) | There is also hotlink protection (a Referer allowlist), proxy or 302 redirect access modes, Cloudflare CDN cache purging, an audit log, alert webhooks and Prometheus metrics. ## When it fits, and when it does not [#when-it-fits-and-when-it-does-not] A good fit: - Personal or small-team use: images for blogs, notes and documentation sites. - You have several storage options (free object storage tiers, cloud drives, VPS disks) and want them to back each other up. - You do not fully trust your storage providers and want them to hold only ciphertext. Not a good fit: - **Horizontal scaling or an HA cluster.** ImgYoung is a single-instance design (SQLite plus an in-process job queue); several processes cannot share one data set. - **A public image hosting service.** There is no open sign-up, payment or billing; only admins can create accounts. - **Private images.** Image links are public: anyone with the link can view the image. There are no signed URLs or per-image access control, and hotlink protection is only a light barrier. See [No private images](./guides/access.mdx#no-private-images). - **Albums, tags and similar organization.** The console offers a list, search, and filters by user, token and time. - **Formats beyond the common four.** Only JPEG, PNG, GIF and WebP are accepted (detected from file content, not the extension); SVG, HEIC, AVIF and others are rejected. ## How to read these docs [#how-to-read-these-docs] If you are new, start with the quick start and get it running in Docker; for a long-running deployment, continue with the installation options and storage channels. Before exposing it to the internet, read [Reverse proxy](./operations/reverse-proxy.mdx) and [Backup and restore](./operations/backup.mdx). From nothing to the Markdown link of your first image in ten minutes. Install script, Docker, Docker Compose, release archives and building from source, compared. Add object storage and cloud drive channels; set priority, encryption and direct links. Reuse the multi-channel storage engine and failover primitives in your own Go project. Every HTTP endpoint, generated from the OpenAPI document. # Quick start (/en/docs/quick-start) Run ImgYoung with Docker, sign in to the console, upload your first image and get its Markdown link. You need a machine with Docker installed (Linux, macOS or Windows) whose port 8721 you can reach from a browser. For installs without Docker, see [Install](./install/index.mdx). ### Start the service [#start-the-service] ```bash title="Terminal" docker run -d --name imgyoung --restart unless-stopped \ -p 8721:8721 \ -v imgyoung-data:/data \ ghcr.io/x-haose/imgyoung:latest ``` - `-v imgyoung-data:/data`: the database, the images in the built-in local channel and the master key `master.key` all live in this volume. Deleting the volume deletes everything. - `--restart unless-stopped`: restoring the metadata database from a snapshot in the console makes the service exit on purpose; Docker starts it again and the snapshot is swapped in on startup. Without a restart policy the container stays stopped after a restore. Check that it started: ```bash title="Terminal" docker logs imgyoung ``` Look for a line with `msg="ImgYoung 已启动"` (log messages are in Chinese). The first start also prints two `level=WARN` lines, `已生成主密钥文件,请立即备份——丢失将导致渠道配置与加密渠道数据永久不可恢复` (a master key file was generated; back it up now) and `已初始化默认管理员密码,请登录后立即在后台修改` (the default admin password was initialized; change it after signing in). Both are expected. ### Sign in and change the password [#sign-in-and-change-the-password] Open `http://:8721/` in a browser (`http://localhost:8721/` on the same machine) and sign in with the factory account: | Username | Password | |---|---| | `admin` | `imgyoung123` | The factory password is public. After the first sign-in you land on the "Set the admin password" page. Until the password is changed no other console page opens, and the server rejects every console API call other than signing out, reading your own account and changing the password (with 403). The new password needs at least 8 characters; click "Save and sign in again", then sign in with the new password. ### Upload your first image [#upload-your-first-image] Pick one: upload in the console, or issue an upload token and upload with curl. 1. Open "Images" in the sidebar and click "Upload". 2. Drag images into the dialog, or press Ctrl / ⌘ + V to paste a screenshot from the clipboard. JPEG, PNG, GIF and WebP are accepted, up to 50 MB per image by default. 3. When the upload finishes, close the dialog, click the image card to open "Image details", and under "Share link" pick **Markdown** next to "Copy as". 1. Open "Upload tokens" in the sidebar, click "Issue token", enter `curl` as the purpose, leave the expiry at "Never", and click "Issue". 2. Copy the token shown in the dialog; it starts with `imgy_`. It is shown only this once, and the dialog only closes after you tick "I have stored this secret safely". 3. Upload: ```bash title="Terminal" curl -H "Authorization: Bearer imgy_your_token" \ -F "file=@screenshot.png" \ http://localhost:8721/api/upload ``` ```json title="Response" {"id":"R0RjUyP1UW","url":"http://localhost:8721/i/R0RjUyP1UW"} ``` `url` is the image address. With jq installed you can get the Markdown directly: ```bash title="Terminal" curl -s -H "Authorization: Bearer imgy_your_token" -F "file=@screenshot.png" \ http://localhost:8721/api/upload | jq -r '"![](\(.url))"' ``` The Markdown looks like this: ```md ![](http://localhost:8721/i/R0RjUyP1UW) ``` Paste it into your blog or notes and the image shows up. Until the public site address (`base_url`) is configured, links are built from the address of the current request: if you opened the console via `localhost`, the copied link points to `localhost` and will not work on other machines. To share links, open the console through its public domain or IP, or set `base_url` under "Settings → Runtime settings". See [Configuration](./configuration.mdx). ## Next steps [#next-steps] A fresh instance has only the built-in `local` channel, which stores images under `blobs/` in the data volume. Metadata snapshots are written to every healthy channel, so for now this disk holds the only copy. If it fails, the images and the snapshots are lost together, and the "Dashboard" warns "Your data only lives on this host". Open "Storage channels" in the sidebar soon and click "New channel" to add an off-site object storage or cloud drive channel. Existing images are replicated to it automatically, and a snapshot is written there on the next scheduled backup or the next start; for a copy right away, click "Back up now" under "Settings → Backups & export". Once the new channel is healthy, the dashboard warning goes away. - [Storage channels](./channels/index.mdx): add an off-site channel, set priority and encryption. - [Backup and restore](./operations/backup.mdx): back up the master key `master.key` separately. It is not part of the snapshots; without it the service refuses to start, and the channel credentials and encrypted data cannot be recovered. - [Reverse proxy](./operations/reverse-proxy.mdx): serve it over HTTPS on your own domain. - [Clients](./guides/clients.mdx): set up PicGo, PicList, Typora and other clients. - [Docker image](./install/docker.mdx): image tags, the data volume and environment variables in full. # Installation (/en/docs/install) Five ways to install, compared - the install script, the Docker image, Docker Compose, release archives and building from source. ImgYoung is a single pure-Go binary with the admin console's front end compiled in; it needs no database server, cache or other process at runtime. All five methods give you the same program. They differ only in how you get it and what supervises it. ## Comparison [#comparison] | Method | Who it suits | Needs root | How to upgrade | Where data lives | |---|---|---|---|---| | [Install script](./script.mdx) | Running directly on a Linux / macOS host; on Linux, handing it to systemd | To install into `/usr/local/bin` or with `--service`; not for `~/.local/bin` | Run the same command again | With `--service`: `/var/lib/imgyoung`; otherwise `data/` under the directory it is started from | | [Docker image](./docker.mdx) | Hosts that already run Docker | Depends on how Docker permissions are set up on the host | Pull the new image and recreate the container | The volume mounted at `/data` in the container | | [Docker Compose](./compose.mdx) | Keeping the deployment in a file, or running it alongside a reverse proxy | Same as above | `docker compose pull`, then `docker compose up -d` | The `imgyoung-data` volume (Compose prefixes it with the project name) | | [Release archive](./binary.mdx) | Windows; environments where running the script is inconvenient; full manual control | No | Download the new archive, replace the binary, restart | `data/` under the directory it is started from, or `IMGYOUNG_DATA_DIR` | | [Build from source](./source.mdx) | Developers who change the code or build it themselves | No | Pull the new code and rebuild | Same as the release archive | Not sure? If you have Docker, use [Docker Compose](./compose.mdx). On a Linux host without Docker, use the [install script](./script.mdx) with `--service`. ## What all methods share [#what-all-methods-share] - **Listening address**: `:8721` by default (port 8721 on every interface); change it with the `IMGYOUNG_LISTEN` environment variable. - **First sign-in**: open `http://:8721/` and sign in with `admin` / `imgyoung123`; the console asks you to change the password right away. - **Data directory**: holds the metadata database `imgyoung.db`, the master key `master.key`, the built-in local channel's `blobs/` and the variant cache `variants/`. Back it up, and back up `master.key` separately in particular. - **Configuration**: a handful of bootstrap settings come from `IMGYOUNG_*` environment variables; all runtime settings are edited in the console and apply on save. There is no config file. See [Configuration](../configuration.mdx). - **Process supervisor**: "Restore" under "Settings → Backups & export" in the console makes the service exit cleanly so the snapshot can be swapped in when the supervisor starts it again. For long-running use, always run it under something that restarts the process after it exits, such as systemd or Docker (`--restart unless-stopped` for Docker, `Restart=always` for systemd). The samples under `deploy/` in the repository are already set up that way. `go install` compiles straight from the repository source, but the admin console's front end is a build artifact that is not committed, so the resulting binary has the API but no console. To install on a machine, use the install script or a release archive; no Go toolchain is needed. What Go developers usually want to reuse is the storage engine, which is a public package you can `go get`. See [Go library](../library/index.mdx). # Install script (/en/docs/install/script) Download a release on Linux or macOS, verify its sha256 and put it on the PATH; on Linux it can also register a systemd service. Running it again upgrades. ## Usage [#usage] ```bash title="Terminal" curl -fsSL https://raw.githubusercontent.com/x-haose/imgyoung/main/install.sh | sh ``` Pass options to the script through `sh -s --`: ```bash title="Terminal" # Install as a systemd service and start it (Linux only, needs root) curl -fsSL https://raw.githubusercontent.com/x-haose/imgyoung/main/install.sh | sudo sh -s -- --service # Install a specific version curl -fsSL https://raw.githubusercontent.com/x-haose/imgyoung/main/install.sh | sh -s -- --version 1.0.0 ``` To read the script before running it: ```bash title="Terminal" curl -fsSLo install.sh https://raw.githubusercontent.com/x-haose/imgyoung/main/install.sh less install.sh sudo sh install.sh --service ``` The script needs `curl`, `tar`, `install`, and either `sha256sum` or `shasum`; `--service` also needs `systemctl`. ## Options and environment variables [#options-and-environment-variables] | Option | Default | Description | |---|---|---| | `--version X.Y.Z` | Latest stable release | Install a specific version; the `v` prefix is optional | | `--dir DIR` | `/usr/local/bin` for root, `~/.local/bin` for other users | Where to install the binary; created if missing, must be writable. With `--service` it must be an absolute path outside `/home`, `/root` and `/run/user` | | `--service` | Not installed | Linux only, needs root: also install and start the systemd service `imgyoung`, with data in `/var/lib/imgyoung` | | Environment variable | Default | Description | |---|---|---| | `IMGYOUNG_RELEASES` | `https://github.com/x-haose/imgyoung/releases` | The releases address. Set it to use a mirror or internal distribution; the layout must match GitHub Releases (see "Mirrors and internal distribution" below) | An unknown option or a malformed version makes the script exit with an error right away, before it changes anything. ## What the script does [#what-the-script-does] 1. **Checks prerequisites.** Confirms `curl`, `tar` and `install` exist; with `--service` it also checks for Linux, root, `systemctl`, and that any `--dir` given is an absolute path outside the home directories. If anything is missing it exits before downloading and leaves the system untouched. 2. **Detects the platform.** `uname -s` must be Linux or Darwin, `uname -m` must be x86_64 / amd64 or aarch64 / arm64; anything else is an error (on Windows, use a [release archive](./binary.mdx)). 3. **Picks the version.** Without `--version` it requests `/latest`, follows the redirect and takes the last path segment of the final URL as the tag. GitHub's latest release never points at a pre-release. 4. **Downloads and verifies.** Downloads `imgyoung___.tar.gz` and that release's `checksums.txt` into a temporary directory and checks the sha256. On a mismatch it aborts without installing anything. The temporary directory is removed when the script exits. 5. **Installs the binary.** Installs `imgyoung` into the target directory with mode 0755 and prints its version. 6. **Installs the service** (`--service` only). Writes the archive's `deploy/imgyoung.service` to `/etc/systemd/system/imgyoung.service` with `ExecStart` pointing at the actual install path, saying so if the content differs from the existing file, then runs `systemctl daemon-reload` and enables it. If the service is already running it is restarted onto the new version; otherwise it is started. 7. **Prints next steps.** Warns if the target directory is not on `PATH`, and prints the sign-in address and factory account. Without `--service` the script only installs the binary and does not start it. Run `imgyoung` yourself; data goes to `data/` under the current directory (or `IMGYOUNG_DATA_DIR`). For long-running use, put it under a process supervisor; see [Installation](./index.mdx) for why. ## The systemd service [#the-systemd-service] `--service` installs this sample from the repository, changing only `ExecStart`: ```ini title="deploy/imgyoung.service" # ImgYoung 的 systemd 服务示例;install.sh --service 安装的就是它。 # 手动安装:二进制放到 /usr/local/bin/imgyoung,本文件复制到 /etc/systemd/system/imgyoung.service, # 然后 systemctl daemon-reload && systemctl enable --now imgyoung。 # 数据在 /var/lib/imgyoung(数据库、本地存储渠道、master.key),备份它。 [Unit] Description=ImgYoung 自部署图床 Documentation=https://github.com/x-haose/imgyoung After=network-online.target Wants=network-online.target [Service] ExecStart=/usr/local/bin/imgyoung Environment=IMGYOUNG_DATA_DIR=%S/imgyoung # 渠道参数直接交给 rclone,不用 rclone 的配置文件;置空让 rclone 启动时不去寻找家目录下的配置 Environment=RCLONE_CONFIG= # 只经本机反向代理对外时,改成只监听回环: # Environment=IMGYOUNG_LISTEN=127.0.0.1:8721 # 以临时系统用户运行,数据目录由 systemd 创建并归属该用户 DynamicUser=yes StateDirectory=imgyoung # 后台「从快照恢复」会让服务优雅退出,靠这里重新拉起后换入快照;正常退出也要拉起 Restart=always RestartSec=3s # 优雅停机先停收新请求、等在途请求至多 6 秒,再停后台任务至多 2 秒;多出的是关库与退出的余量 TimeoutStopSec=15s # 数据目录里有 master.key,新建文件只给运行用户自己读写 UMask=0077 # 进程只能写自己的数据目录;本地存储渠道指向别处时,用 ReadWritePaths= 放开那个目录 NoNewPrivileges=yes CapabilityBoundingSet= ProtectSystem=strict ProtectHome=yes PrivateTmp=yes PrivateDevices=yes ProtectKernelTunables=yes ProtectKernelModules=yes ProtectControlGroups=yes ProtectKernelLogs=yes ProtectClock=yes ProtectHostname=yes RestrictNamespaces=yes RestrictRealtime=yes RestrictSUIDSGID=yes LockPersonality=yes SystemCallArchitectures=native [Install] WantedBy=multi-user.target ``` The settings that matter: - **Data directory.** `IMGYOUNG_DATA_DIR=%S/imgyoung` resolves to `/var/lib/imgyoung`, which `StateDirectory=imgyoung` makes systemd create and hand to the service user. With `DynamicUser` the real directory is `/var/lib/private/imgyoung` and `/var/lib/imgyoung` is a symlink to it. It holds `imgyoung.db`, `master.key`, the local channel's `blobs/` and the variant cache `variants/`. - **DynamicUser=yes.** systemd allocates a transient system user on every start, so no user has to be created in advance. Together with sandboxing such as `ProtectSystem=strict` and `ProtectHome=yes`, the process can write only its own data directory: if you point the local channel somewhere else, open that directory with `ReadWritePaths=`; and the binary must not live under `/home` or `/root`, or the service cannot start; the script rejects such a `--dir`. - **Restart=always.** Restoring a snapshot in the console makes the service exit cleanly; systemd starts it again 3 seconds later and the snapshot is swapped in before the database is opened. `Restart=on-failure` does not restart a clean exit, so the restore would only complete once someone starts the service by hand. - **TimeoutStopSec=15s.** On stop, the service first stops accepting new requests and waits up to 6 seconds for in-flight ones (forcibly closing what remains), then cancels background loops and in-flight replication jobs and waits up to 2 seconds, then closes the database and exits. That is about 8 seconds in total; 15 leaves headroom. Cancelled replication jobs are re-queued by reconciliation on the next start. - **UMask=0077.** New files, `master.key` included, are readable and writable only by the service user. - **RCLONE_CONFIG=** (empty). ImgYoung hands channel parameters to rclone directly and never reads an rclone config file. Everyday commands: ```bash title="Terminal" systemctl status imgyoung # service status journalctl -u imgyoung -f # follow the logs sudo systemctl restart imgyoung # apply changed environment variables ``` ### Changing the service configuration [#changing-the-service-configuration] Re-running the script with `--service` overwrites `/etc/systemd/system/imgyoung.service` with the version from the release archive, and says so when the content changes. Keep local changes out of that file and put them in a drop-in instead: ```bash title="Terminal" sudo systemctl edit imgyoung ``` In the editor that opens, add the settings to override. For example, to listen on loopback only when a local reverse proxy is the sole entry point: ```ini title="/etc/systemd/system/imgyoung.service.d/override.conf" [Service] Environment=IMGYOUNG_LISTEN=127.0.0.1:8721 ``` Save, then run `sudo systemctl restart imgyoung`. The available environment variables are listed in [Configuration](../configuration.mdx). ## Upgrading [#upgrading] Run the same command you installed with: - Installed with `--service`: the binary is replaced, the unit file is rewritten and the service is restarted onto the new version. You must pass `--service` again; otherwise only the file on disk changes and the running process stays on the old version. - Installed without `--service`: only the binary is replaced; restart the process yourself. Use `--version` to stay on a particular version. The database schema is migrated automatically when the new version starts. `--version` can also install an older version, but that is not a downgrade: an older version refuses to start on a database migrated by a newer one, and the error names the latest migration in the database and in the binary. Rolling back takes the older version plus a snapshot taken before the upgrade; see [Upgrading](../operations/upgrade.mdx) for the steps and what to prepare beforehand. ## Uninstalling [#uninstalling] With the service installed: ```bash title="Terminal" sudo systemctl disable --now imgyoung sudo rm /etc/systemd/system/imgyoung.service sudo rm -rf /etc/systemd/system/imgyoung.service.d # only exists if you used systemctl edit sudo systemctl daemon-reload sudo rm /usr/local/bin/imgyoung ``` The data directory is left in place. Delete it only once you are sure you no longer need the images, the database and `master.key` in it: ```bash title="Terminal" sudo rm -rf /var/lib/private/imgyoung /var/lib/imgyoung ``` If only the binary was installed, delete it (`~/.local/bin/imgyoung` or `/usr/local/bin/imgyoung` by default), then delete whichever data directory you ran it with, if you no longer need it. ## Mirrors and internal distribution [#mirrors-and-internal-distribution] If GitHub is hard to reach, host the release files on a mirror or an internal server and point `IMGYOUNG_RELEASES` at it. The variable has to reach the `sh` that runs the script, not `curl`: ```bash title="Terminal" curl -fsSL https://mirror.example.com/imgyoung/install.sh \ | sudo IMGYOUNG_RELEASES=https://mirror.example.com/imgyoung/releases sh -s -- --service ``` `install.sh` itself must be reachable there too. The mirror must use the same layout as GitHub Releases: ```text /latest redirects to .../tag/vX.Y.Z /download/vX.Y.Z/imgyoung_X.Y.Z_linux_amd64.tar.gz /download/vX.Y.Z/checksums.txt ``` `/latest` is only used without `--version`: the script follows the redirect and treats the last segment of the final URL as the version, which must start with `v` followed by a digit. If the mirror cannot redirect like that, always pass `--version` when installing and upgrading. # Docker image (/en/docs/install/docker) Run ImgYoung from the multi-arch image on GHCR - the run command, image tags, the data volume, environment variables and settings behind a reverse proxy. ## Running it [#running-it] ```bash title="Terminal" docker run -d --name imgyoung --restart unless-stopped \ -p 8721:8721 \ -v imgyoung-data:/data \ ghcr.io/x-haose/imgyoung:latest ``` Open `http://:8721/` and sign in with `admin` / `imgyoung123`; the first sign-in asks you to change the password right away. | Flag | Purpose | |---|---| | `-d --name imgyoung` | Run in the background as a container named `imgyoung`, which later commands refer to | | `--restart unless-stopped` | Start the process again after it exits; see "Restart policy" below | | `-p 8721:8721` | Publish the service port. If a reverse proxy on the same host is the only entry point, use `-p 127.0.0.1:8721:8721` so 8721 is not exposed directly | | `-v imgyoung-data:/data` | A named volume holding all state | The image is published for `linux/amd64` and `linux/arm64`; Docker picks the right one for the host. To see the version inside an image: ```bash title="Terminal" docker run --rm ghcr.io/x-haose/imgyoung:latest -version ``` ## Image tags [#image-tags] The image is `ghcr.io/x-haose/imgyoung`. Tags have no `v` prefix: | Tag | Points to | Good for | |---|---|---| | `1.2.3` | One specific version; never changes after release | Production, where you decide exactly when to upgrade | | `1.2` | The latest stable release in the 1.2 series; moves when a patch release ships | Getting fixes automatically without crossing a minor version | | `latest` | The latest stable release | Trying it out and personal instances | Pre-releases get only their exact version tag and never move the major.minor tags or `latest`, so instances pinned to those never pull a pre-release. ## The data volume [#the-data-volume] The data directory inside the container is `/data` (the image sets `IMGYOUNG_DATA_DIR=/data`). It contains: | Path | Contents | |---|---| | `imgyoung.db` | The metadata database (SQLite in WAL mode, so `imgyoung.db-wal` and `imgyoung.db-shm` sit next to it while running): users, image records, channel configuration (credentials encrypted with the master key), runtime settings and the job queue | | `master.key` | The master key, generated with mode 0600 when a brand-new instance first starts without `IMGYOUNG_MASTER_KEY`. When `IMGYOUNG_MASTER_KEY` is set, this file is neither created nor read | | `blobs/` | Objects of the built-in local channel. While the local channel is healthy, metadata snapshots are also written here, under `_imgyoung/backup/` | | `variants/` | The cache of variants produced by on-demand processing, capped at 10240 MB by default and evicted least-recently-used first | During a restore from snapshot you may also see the staged file `imgyoung.db.restore`, and afterwards the previous database kept as `imgyoung.db.pre-restore-