Configuration

Every bootstrap setting (environment variable) and runtime setting (admin console), with defaults, valid ranges and when changes take effect, plus how to keep the master key safe.

Two kinds of settings

ImgYoung has exactly two kinds of settings. There is no configuration file, and the only command-line flag is -version (prints version, commit and build time, then exits).

Bootstrap settingsRuntime settings
WhatWhat the process needs before it can start: listen address, data directory, master key, trusted proxies, networks allowed on /metricsEvery other operational parameter
SourceEnvironment variables IMGYOUNG_* onlyThe settings table in the metadata database; environment variables are not read
ChangingChange the variable and restart the processEdit under Settings in the admin console; takes effect on save, no restart
Where to see themThe read-only Bootstrap configuration card at the bottom of Settings → Runtime settingsSettings in the admin console

The split exists so that "where does this value come from" always has exactly one answer: environment variables only get the process started, and some of them also set trust boundaries; runtime settings have a single writer, the admin console, with no seeding from environment variables and no configuration file.

Runtime settings live in the metadata database, so they are included in metadata snapshots and restored with them. Configure a new instance once in the admin console; settings added by an upgrade are filled in with their factory defaults by the database migration.

Bootstrap settings

VariableDefaultFormatPurpose
IMGYOUNG_LISTEN:8721host:port, e.g. 127.0.0.1:8721 or [::1]:8721; port 0 lets the OS pick oneHTTP listen address. ImgYoung only speaks plain HTTP; HTTPS is the reverse proxy's job
IMGYOUNG_DATA_DIRdata (relative to the working directory)Directory path; created with mode 0750 if missingData directory: metadata database imgyoung.db, master key file master.key, the built-in local channel's blobs/, and the variant cache variants/
IMGYOUNG_MASTER_KEYEmpty (use master.key in the data directory)64 hex characters (32 bytes)Master key. Checked against the master key fingerprint stored in the database at every start; a missing or mismatched key refuses to start. See Master key below
IMGYOUNG_TRUSTED_PROXIES127.0.0.0/8,::1/128Comma-separated CIDRs or single IPs; a single IP means a network containing only that addressX-Forwarded-For / X-Forwarded-Proto / X-Forwarded-Host are honored only when the connecting peer is in one of these networks; see Reverse proxy & HTTPS
IMGYOUNG_METRICS_ALLOW127.0.0.0/8,::1/128,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16,fc00::/7Same as aboveClients in these networks may read /metrics; everyone else gets 403. See Monitoring & alerts
  • An unset variable takes its default; a variable set to an empty string (or whitespace only) is explicitly empty. Surrounding whitespace is trimmed from every value. What empty means:

    • IMGYOUNG_TRUSTED_PROXIES empty: forwarded headers are never trusted;
    • IMGYOUNG_METRICS_ALLOW empty: /metrics is open to no one, every request gets 403;
    • IMGYOUNG_MASTER_KEY empty: same as unset, master.key in the data directory is used;
    • IMGYOUNG_LISTEN and IMGYOUNG_DATA_DIR do not accept an empty value; setting either to an empty string fails startup, and the error names the variable. A compose file that says ${VAR} while the host has no such variable hands the container exactly that empty string; to get the default, leave the variable unset.

    The Bootstrap configuration card in the admin console shows an empty network list as "Empty: forwarded headers are never trusted" or "Empty: open to no one".

  • Both network lists are parsed at startup. An entry that is neither a CIDR nor an IP fails startup, and the error names the variable and the bad entry. The listen address is checked when the port is bound and the master key when it is parsed; invalid values also fail startup.

  • The Docker image sets IMGYOUNG_DATA_DIR=/data; deploy/imgyoung.service sets it to /var/lib/imgyoung.

Where to set environment variables for each deployment:

Terminal
IMGYOUNG_LISTEN=127.0.0.1:8721 IMGYOUNG_DATA_DIR=/srv/imgyoung ./imgyoung

Runtime settings

Runtime settings are edited under Settings in the admin console, which only administrators can see. Allow empty referer sits on the Hotlink protection tab; everything else is on the Runtime settings tab, grouped as below.

  • Saving submits only the fields you changed. All changes are validated together; if any value is invalid, none of them is applied and the reason is shown.
  • Changes apply immediately: background loops re-time themselves with a new interval right away instead of finishing the old one.
  • Every applied change writes an audit log entry (action config.update, with old and new values); the Cloudflare API token is only recorded as set or empty.
  • The same operations are available as GET /api/admin/config/full and PUT /api/admin/config, whose body is a JSON object mapping setting keys to new values; see the API reference.
  • Every duration setting is capped at 100 years (876,000 hours), converted to its own unit: 36,500 days, 876,000 hours, 52,560,000 minutes, 3,153,600,000 seconds, or 3,153,600,000,000 milliseconds; larger values are rejected on save. The "Valid values" column below lists only the lower bound for these settings.

"Default" below is the factory value; "Valid values" is what the server accepts.

KeyUI labelDefaultValid valuesNotes
access_modeAccess modeproxyproxy / redirectproxy: images are read from a channel and served by ImgYoung; a failed read moves on to the next channel immediately. redirect: original-image requests get a 302 to a channel that allows direct links, sent with Cache-Control: no-store; see CDN
base_urlPublic site addressEmptySite root with scheme, e.g. https://img.example.com; a trailing / is removed on saveEmpty means it is derived from each request. Required when the public domain differs from what reaches ImgYoung through a CDN or proxy, for CDN cache purging, and for one-click cloud drive authorization; see Reverse proxy & HTTPS
min_successMinimum synced replicas1≥ 1Channels that must be written synchronously before an upload succeeds; if the healthy channels cannot provide that many, the upload fails with 503
encrypt_new_channelsEncrypt new channels by defaultOffOn / offWhether a new channel is encrypted when the form does not choose explicitly; encryption is fixed when the channel is created

Upload limits

KeyUI labelDefaultValid valuesNotes
max_upload_mbMax image size50≥ 1, MB (1 MB = 1024 × 1024 bytes)Applies to the image itself: a file exactly at the limit is accepted, anything larger gets 413; a multipart request body is additionally capped at "this limit + 64 KiB". Also caps URL imports, S3 uploads, and the source image read for on-demand processing: after lowering it, older images above the new limit fall back to the original instead of being resized or converted
max_pixels_mpPixel budget50≥ 1, megapixelsImages whose width × height exceeds max_pixels_mp × 1,000,000 are rejected (422)
max_concurrent_uploadsConcurrent uploads8≥ 1One global limit shared by data-plane uploads, URL imports, admin console uploads and every request on the S3 endpoint; when full, requests get 429 (Retry-After: 1) immediately
upload_rate_per_minUpload rate limit0≥ 0, per minutePer user, shared between tokens and admin console uploads; burst equals the per-minute allowance. Excess requests get 429 (Retry-After: 60). 0 disables it

Lifecycle

KeyUI labelDefaultValid valuesNotes
default_ttl_hoursDefault lifetime0≥ 0, hoursUsed when an upload sets no ttl and the user has no default of their own. 0 means never expires
ttl_renew_on_accessRenew on accessOnOn / offA public access (GET) to a time-limited image that has not yet expired moves its expiry to the time the access is flushed plus its lifetime. An image past its expiry is unreachable right away and is never renewed
cold_cleanup_daysCold image threshold90≥ 0, daysImages without public access for this long go to Cleanup for a manual decision; nothing is deleted automatically. 0 disables marking
delete_grace_minutesDelete grace period0≥ 0, minutesWait between a stored image losing its last reference and its channel objects being deleted. The deletion time is fixed when the reference count reaches zero, using the value at that moment; later changes do not affect it. 0 deletes on the next cleanup sweep
cleanup_sweep_secondsCleanup sweep interval60≥ 1, secondsCadence for expiring references, physical deletion, cold-image marking, and reclaiming idempotency keys and audit log entries
activity_flush_secondsActivity flush interval60≥ 1, secondsInterval for batch-writing access counts, last-access times and variant cache access times
idempotency_key_ttl_hoursIdempotency key retention24≥ 1, hoursUpload retries carrying the same Idempotency-Key within this window return the first result; older keys are reclaimed

On-demand processing

KeyUI labelDefaultValid valuesNotes
process_widthsWidth tiers[100,200,400,800,1600]Array of positive integers, pixels; may be empty?w= accepts only these values, anything else gets 400; empty disallows resizing
process_formatsTarget formats["webp","jpeg","png"]Non-empty subset of webp, jpeg, png?format= accepts only these values. With webp included, browsers that support WebP automatically get WebP for JPEG / PNG images
exif_policyDefault EXIF policypassthroughpassthrough (keep) / strip (remove metadata)Default EXIF handling at upload; a single upload can override it with the exif_policy form field; does not apply to S3 uploads, which are stored byte for byte
process_quality_webpWebP quality800–100
process_quality_jpegJPEG quality851–100
process_webp_methodWebP effort40–6Higher is slower and smaller
process_workersProcessing concurrency4≥ 1Concurrent processing jobs; time spent waiting for a slot counts toward the timeout
process_timeout_msProcessing timeout5000≥ 1, millisecondsIncludes queueing; on timeout the original is served with Cache-Control: no-store
variants_cache_mbVariant cache limit10240≥ 1, MBVariants are cached in <data dir>/variants/; beyond the limit the least recently accessed are evicted

Deduplication

KeyUI labelDefaultValid valuesNotes
dedup_scan_secondsDedup scan interval300≥ 1, secondsEach round first merges byte-identical images automatically, then scans for similar ones
dedup_phash_max_distanceSimilarity threshold10≥ 0, Hamming distanceImages whose perceptual hashes are at most this far apart become Duplicates candidates for a manual decision. 0 disables similarity scanning; identical images are still merged

Reliability & channels

KeyUI labelDefaultValid valuesNotes
health_check_secondsHealth check interval60≥ 1, secondsCadence of channel health checks and alert evaluation
backup_interval_hoursBackup interval24≥ 1, hoursInterval between automatic metadata snapshots. At startup, a backup runs right away if any healthy channel lacks a snapshot within this interval. See Backup & restore
backup_keepBackups to keep7≥ 1Snapshots are written to every channel that is healthy and not being cleaned up; after each write, that channel keeps only the newest this many
usage_report_secondsUsage report interval300≥ 1, secondsOnly controls how often the imgyoung_channel_storage_bytes metric is refreshed; usage alerts are evaluated on the health check interval
channel_quota_gbChannel usage alert threshold0≥ 0, GB (1 GB = 2³⁰ bytes)Opens an alert when a channel's stored data (sum of image sizes of its completed replicas) exceeds this; writes are not blocked. 0 disables the alert

CDN cache purge

KeyUI labelDefaultValid valuesNotes
cdn_cloudflare_zone_idCloudflare Zone IDEmptyText
cdn_cloudflare_api_tokenCloudflare API tokenEmptyTextNeeds the Cache Purge permission. Stored encrypted with the master key and never shown by any API, page or log. Leaving the field empty keeps the current value; Clear token removes it

Purging is enabled only when the Zone ID, the API token and base_url are all set. The badge next to the group title reads Purge enabled or Purge disabled, and the startup log prints the same state. See CDN.

Alerts & audit

KeyUI labelDefaultValid valuesNotes
alert_webhook_urlAlert webhook URLEmptyEmpty, or an http / https URL with a hostReceives a JSON POST when an alert opens or resolves; payload format in Monitoring & alerts. Empty means alerts are recorded but not sent
audit_retention_daysAudit log retention0≥ 0, daysCleanup sweeps delete audit entries older than this. 0 keeps them forever
KeyUI labelDefaultValid valuesNotes
allow_empty_refererAllow empty refererOnOn / offWith a non-empty Referer allowlist, whether requests without a Referer (typed into the address bar, download tools) are allowed. With an empty allowlist hotlink protection is off and this has no effect

The allowlist rules are managed on the same tab and are not part of the settings table.

Master key

The master key is 32 random bytes. It is used to:

  • encrypt channel configurations (including each channel's credentials) and the Cloudflare API token before they are stored;
  • derive the crypt password and salt of every encrypted channel;
  • derive the S3 secret of every upload token (never stored, recomputed on demand).

Upload tokens and login passwords are stored as hashes and do not depend on the master key.

Generation and verification

At startup the master key is taken from IMGYOUNG_MASTER_KEY when it is non-empty, in which case master.key is neither read nor created; otherwise it is read from master.key in the data directory. The key is then checked against the master key fingerprint recorded in the database. The fingerprint lives in the single-row table master_key_fingerprint; it is derived from the master key with HKDF-SHA256 and cannot be reversed into the key.

  • No fingerprint in the database yet (a brand-new database, or one that has never recorded a fingerprint): if neither source has a key, master.key is generated in the data directory (64 hex characters plus a newline, mode 0600, written to a temporary file and renamed) and a warning is logged telling you to back it up; the key's fingerprint is then written to the database.
  • A fingerprint is already recorded: the master key must be present and must match. If IMGYOUNG_MASTER_KEY is unset or empty and there is no master.key in the data directory, or the key does not match the fingerprint, startup is refused with an explanation, and no new key is generated. Only a brand-new database ever gets a generated master.key.
  • If master.key exists but is not valid 64-character hex, startup fails rather than generating a new key: a new key would make every existing ciphertext useless.

To generate a master key yourself:

Terminal
openssl rand -hex 32

When switching from the file to the variable, copy the contents of master.key into the variable verbatim; if you get it wrong, the fingerprint check fails and the process refuses to start.

Keeping it safe

The master key is not in metadata snapshots and not in the metadata export. Back it up separately, somewhere other than the server and the snapshots. Copy it right after the first start:

Terminal
cp data/master.key /safe/place/imgyoung-master.key

Losing it, or starting with the wrong one

The master key cannot be rotated. Once the database has recorded the fingerprint, both a wrong key and a missing key are stopped at startup:

  • A different master key (for example a mistyped IMGYOUNG_MASTER_KEY): startup is refused and the error tells you to switch back to the original key; with the original key back, the process starts normally.
  • A missing master key (for example master.key was deleted from the data directory, or a ${VAR} in the compose file is not set on the host): startup is refused and no new key is generated; put the original key back and the process starts normally.

Actually losing the master key is irreversible: the database can no longer be started, and nothing encrypted with the key can be recovered:

  • channel configurations, including each channel's credentials, cannot be decrypted;
  • data on encrypted channels, including metadata snapshots stored there, can never be decrypted;
  • the Cloudflare API token cannot be decrypted;
  • the S3 secret of every upload token can no longer be computed.

Back up master.key first

Metadata snapshots, the export and the images in your channels cannot bring back a lost master key. For disaster recovery, the new machine must have the same master key in place before its first start; after restoring another instance's snapshot, switch to that instance's master key, or the fingerprint does not match and startup is refused. See Backup & restore.

On this page