Skip to content

Configuration

Artifact storage

Where downloaded APKs and their metadata are cached — a directory or S3.

The api downloads an APK from its source once, parses it (package, version, label, icon, ABIs) and keeps the file and metadata in an artifact store. Agents fetch the APK from the api, never from the source.

The store is a cache: sources stay the source of truth, and anything missing is downloaded again on demand, so it needs no backup. The api’s database is separate.

storage:
type: filesystem
filesystem:
dir: /var/cache/holtdroid
cache:
maxSize: 20GiB
warm: 10

Each api replica keeps its own copy. An emptyDir is enough (the chart’s default): after a restart the cache fills again as people browse.

Use a bucket when you run several api replicas, want warm caches across restarts, or don’t want APKs on node disks. Anything S3-compatible works — AWS S3, MinIO, Ceph RGW, Garage, VersityGW, Cloudflare R2.

storage:
type: s3
s3:
endpoint: https://s3.example.com
region: us-east-1
bucket: holtdroid
prefix: cache/
accessKey: env:S3_ACCESS_KEY
secretKey: env:S3_SECRET_KEY
pathStyle: true
Key Meaning
endpoint Base URL; https:// enables TLS
region Bucket region, if your store cares
bucket Existing bucket
prefix Optional key prefix, to share a bucket
accessKey Secret reference; leave both keys empty to use IAM roles / instance profile
secretKey Secret reference
pathStyle true for most self-hosted stores (endpoint/bucket/key)
credentialsURL Fetch temporary keys instead, in the ECS container credentials format (e.g. from holtdroid-s3-sync’s broker); refreshed before they expire
credentialsToken Secret reference sent as the Authorization header to credentialsURL

The api needs GetObject, PutObject, DeleteObject and ListBucket on the bucket/prefix.

Key Default Meaning
cache.maxSize 20GiB Beyond this the least recently used APKs are evicted
cache.warm 10 Newest uncached APKs per listing to download and keep in the background

On S3, “least recently used” means “oldest upload”, since objects can’t be touched cheaply; a bucket lifecycle rule is a fine alternative to maxSize.

Eviction removes only the APK. An app’s name, version and icon stay in the cache, so after a restart (or an eviction) the list shows every app at once. APKs that have no metadata yet are downloaded once in the background and parsed without being kept; only the newest cache.warm are kept. An entry is valid while the file’s revision (its ETag, a content hash on S3) and the parser version match, otherwise the APK is parsed again.

One prefix per artifact revision, named after a hash of source, file and revision — a replaced file gets a fresh entry:

<hash>/app.apk # may be missing after eviction; downloaded again on launch
<hash>/icon.png # when the launcher icon could be rendered
<hash>/meta.json # written last; marks the entry complete