Deploying Konifer
This guide takes Konifer from the in-memory quickstart to a persistent deployment. It is platform-neutral: the same components can run with Docker, Docker Compose, Kubernetes, or another container orchestrator.
A production deployment has three main components. Konifer processes requests; PostgreSQL and image storage hold the durable state:
Konifer itself is replaceable. PostgreSQL and the object store contain the state that must survive container replacement.
Before you begin
You need:
- A container runtime or orchestrator
- PostgreSQL with permission to enable the
ltreeextension or theltreeextension already enabled - An S3 or S3-compatible bucket, or a persistent filesystem mount
- A public URL for Konifer (if exposing over the public internet)
- A secure way to supply credentials and signing secrets
The repository includes a Docker Compose example that starts Konifer, PostgreSQL, and MinIO. It is useful for evaluating the persistent architecture, but its example credentials and exposed database and MinIO ports are not a production configuration.
1. Prepare PostgreSQL
Create a database and application user according to your PostgreSQL provider's instructions. Enable ltree in the
database before starting Konifer:
CREATE EXTENSION IF NOT EXISTS ltree;
Konifer applies its bundled database migrations during startup. The application user therefore needs permission to create and alter tables, indexes, and other schema objects in the application database, in addition to ordinary read/write access.
Back up PostgreSQL as part of the deployment. Its records refer to content stored in the object store, so plan to restore both stores to a compatible point in time.
2. Prepare content storage
S3 or an S3-compatible service
Create every bucket referenced by your path configuration before starting Konifer. Konifer reads, writes, and deletes objects, but it does not create S3 buckets.
Grant the service identity only the object permissions it needs for those buckets. When running against AWS, you can omit static credentials and use the AWS default credential provider chain. Other S3-compatible services may require an endpoint, access key, secret key, region, and path-style addressing.
Filesystem storage
Filesystem storage is suitable when the content directory is mounted persistently into the container. Every Konifer instance that serves the same PostgreSQL database must see the same files at the configured mount path.
For multiple replicas, shared object storage is usually the simpler model. See Storing assets and variants for provider-specific configuration.
3. Create konifer.conf
Create a configuration file outside the container. The following example uses PostgreSQL and an S3-compatible object store:
data-store {
provider = postgresql
postgresql {
host = "postgres.internal"
port = 5432
database = "konifer"
ssl-mode = "require"
}
}
object-store {
provider = s3
s3 {
endpoint-url = "https://s3.example.com"
access-key = "konifer"
region = "us-east-1"
force-path-style = true
}
}
http {
public-url = "https://images.example.com"
}
paths {
"/**" {
object-store {
bucket = "konifer-assets"
}
}
}
Set http.public-url to the externally visible origin. Konifer uses it when it creates asset links; it does not need to
match the internal container address.
force-path-style is required by some S3-compatible providers, including the MinIO configuration in the repository.
Omit the custom endpoint and static access key when your runtime supplies AWS credentials through its workload identity.
See the Configuration reference for all available properties and defaults.
4. Supply secrets
Do not store passwords or signing keys in konifer.conf. Konifer supports environment-variable overrides for these
values:
PG_USER=konifer
PG_PASSWORD=replace-with-a-database-password
S3_SECRET_KEY=replace-with-an-object-store-secret
URL_SIGNING_SECRET_KEY=replace-with-a-signing-secret
Only provide URL_SIGNING_SECRET_KEY when URL signing is enabled. In an orchestrator, supply the same values through
its secret-management mechanism instead of committing an environment file.
5. Start the container
Mount the configuration file at /app/config/konifer.conf:
docker run \
--name konifer \
--restart unless-stopped \
--publish 127.0.0.1:8080:8080 \
--env-file /secure/path/konifer.env \
--mount type=bind,source=/etc/konifer/konifer.conf,target=/app/config/konifer.conf,readonly \
ghcr.io/dmaiken/konifer:0.10.3
Binding to 127.0.0.1 assumes that a reverse proxy on the same host accepts external traffic. Change the published
address to match your network design.
For reproducible deployments, use only released versions of Konifer. Konifer follows Semantic Versioning; see the Konifer release list for available versions.
ghcr.io/dmaiken/konifer:0.10.3
Konifer supports Linux amd64 and arm64 container images.
6. Check application health
The health endpoint returns 200 OK after the application has started:
curl --fail 'http://127.0.0.1:8080/health'
{"status":"okay"}
Use /health for container and load-balancer checks. It reports the Konifer application lifecycle; it is not a
continuous probe of PostgreSQL or object-storage health.
7. Expose Konifer safely
Terminate TLS at a reverse proxy, ingress, or load balancer. Configure its request-size and timeout limits to accommodate
the maximum uploads allowed by source.multipart.max-bytes and source.url.max-bytes.
Konifer does not replace your application's authentication or authorization layer. Keep store, update, delete, and rule evaluation endpoints behind a trusted gateway or private network. If fetch URLs are public and accept arbitrary transformations, enable URL signing to prevent callers from modifying transformation parameters or using your service for unapproved processing.
Decide deliberately which responses a CDN may cache. Konifer always supports ETags for content responses, while
Cache-Control behavior is configured by path. See HTTP caching.
8. Size processing resources
Image processing uses JVM heap, direct memory, native libvips memory, CPU, and temporary disk space. A container memory limit must leave room for native processing in addition to the JVM.
Konifer writes temporary processing files under /app/tmp. The container's writable layer works initially, but a fast
temporary volume or appropriately sized tmpfs is preferable for sustained or high-volume workloads. Large images may
require disk-backed temporary storage rather than RAM.
Start with conservative request limits and variant-worker concurrency, then load-test with representative source images and transformations. See Image processing architecture for memory regions, temporary storage, and worker configuration.
9. Add upload rules when needed
Normal image storage and transformation do not require model files. Upload Rules and the Rule Evaluation API use a SigLIP2 model pack that must be downloaded separately and mounted at:
/app/models/siglip2-base-patch16-224
Follow Installing model files before enabling either feature. Model inference also changes the CPU and memory requirements of the deployment.
Scaling to multiple instances
Konifer natively supports clustered deployment models. Before adding replicas, confirm that every instance uses:
- The same
konifer.confand secrets - The same PostgreSQL database
- The same S3 buckets or shared filesystem
- The same model files when upload rules are enabled
- A load balancer that removes instances when
/healthfails
Configuration is loaded at startup, so roll all instances after changing path rules, profiles, storage settings, or URL signing configuration.
Backups and upgrades
Treat PostgreSQL and object storage as one application data set. Back up both, test restoration, and retain the configuration used to interpret the restored data.
Konifer applies database migrations when it starts. For an upgrade:
- Read the release notes and record the currently deployed image digest.
- Back up PostgreSQL and the object store.
- Start the new version with one instance and allow its migrations to finish.
- Check
/health, logs, uploads, original-image retrieval, and transformed-image retrieval. - Complete the rollout only after those checks pass.
Do not assume that rolling the container back also reverses a database migration. Confirm schema compatibility before returning to an older image.
Production checklist
Before accepting production traffic, verify that:
IN_MEMORYis not enabled. If enabled, Konifer will emit warnings logs.- PostgreSQL and object storage are persistent and backed up.
- The
ltreeextension is installed. - Every configured S3 bucket exists.
- Secrets are supplied outside
konifer.confand are not committed to source control. http.public-urlmatches the public origin.- TLS and access control are enforced by the surrounding platform.
- Public transformation URLs are signed when appropriate.
- Upload-size, CPU, memory, and temporary-storage limits have been tested.
/health, container restarts, application logs, and dependency failures are monitored.