Connect a destination
- Dashboard
- CLI
Go to Settings, Storage destinations, Add destination. Pick a provider, fill in the fields it asks for, and leave “Test the connection before saving” on.
Everything except AWS uses path-style addressing by default. Pass
--virtual-hosted (or tick the box in the dialog) for a provider that needs the bucket in the hostname.
What the connection test does
The test writes a small random object under.probe/, reads it back, compares it, and deletes it. Each step is reported, and a failure carries a stable reason: invalid_credentials, access_denied (valid key, missing permission), bucket_not_found, region_mismatch, endpoint_blocked, tls_error, or unreachable. The key needs permission to put, get, delete, and list objects in the bucket.
Private endpoints
Outbound connections to a destination go through the same SSRF guard as webhooks and log drains: loopback, private, link-local, and other internal addresses are refused after DNS resolution. A MinIO on a private network needsAPP_NOTIFY_ALLOW_PRIVATE_NETWORKS=true on the control plane.
Provider walkthroughs
- Cloudflare R2
- AWS S3
1
Create a bucket
In the Cloudflare dashboard, create a bucket under R2 (for example
app-logs).2
Create an API token
Create an R2 API token with Object Read and Write scoped to that bucket. Copy the access key id, the secret access key, and your account id (shown on the R2 overview page).
3
Add the destination
In Levelrail, add a destination with provider Cloudflare R2, the account id, the bucket, and the key pair. The endpoint is filled in for you and the region is
auto.4
Run the connection test
Every step should be green.
5
Start archiving
Under an app’s Logs, Archive tab (or Settings, Storage destinations for every app), choose the destination and click Start archiving.
Log archive
Log archive ships node-local container logs to a destination as gzip-compressed NDJSON, on a schedule or on demand. See Log archive for what gets archived, retention, dump, and how to retrieve archived objects.Build cache
BuildKit can keep its layer cache in a storage destination, so a rebuild skips unchanged steps even after the build host’s local cache is gone. It uses BuildKit’ss3 cache backend, so it works with every provider above.
Dashboard: an app’s Settings, Deploy settings, Build cache card, or Settings, Storage destinations, Build cache for all apps for a default that every app without its own setting inherits. Pick a destination and an export mode, and save.
- Per-app prefix: layers live under
build-cache/<app>/(change the root withAPP_BUILD_CACHE_PREFIX), so apps never share cache and one app can be cleared without touching another. An app can opt out of the global default by saving a setting with the cache disabled (--disable). - Mode:
max(the default) caches every layer and gives the fastest rebuilds.mincaches only the final image layers and keeps the bucket smaller. - Credentials: they come from the destination’s encrypted secrets, are passed to BuildKit for the one build, and are never logged, stored in build logs, or returned by the API.
- Fails open: if the cache cannot be read or written (bad credentials, bucket gone, network), the build carries on without it. The reason is written to the build log as a warning, shown on the deploy attempt (
cache_warning), and kept on the setting as the last build result. A build that genuinely fails is still a failed build.
More behavior and limits
Private endpoints
Private endpoints
BuildKit dials the bucket itself, so the SSRF guard is applied to a custom endpoint before each build. An endpoint that resolves to an internal address is skipped with a warning unless
APP_NOTIFY_ALLOW_PRIVATE_NETWORKS=true.Remote build nodes
Remote build nodes
A build dispatched to a dedicated build node does not receive bucket credentials, so it runs without the s3 cache and records a warning. Local builds on the control plane use it. Railpack builds do not use the s3 cache yet; Dockerfile builds do.
Hygiene and clearing the cache
Hygiene and clearing the cache
The app card and
build-cache show list the app’s prefix (bounded to APP_BUILD_CACHE_STATS_MAX_OBJECTS, default 2000) for object count, size, and last export time, plus the last build outcome. Clear cache deletes the prefix in pages, at most APP_BUILD_CACHE_CLEAR_MAX_OBJECTS (default 5000) objects per call. When it reports more remaining, run it again. Removing a setting stops using the cache but leaves the objects, so clear first if you want the space back, or add a bucket lifecycle rule on build-cache/.Alerting
Create alog_archive_stale alert rule (dashboard: Alerts, or levelrail-cli apps alerts create <app> --kind log_archive_stale). It fires when any enabled policy’s last run failed, or when a policy has gone longer than the rule’s for_duration without a success (default: three intervals, at least two hours). It notifies through the same channels as every other rule.
API and MCP
REST routes live under/api/v1/storage, /api/v1/log-archive, and /api/v1/build-cache (see the API reference). Reads need the read ability, policy changes and dumps need write, and anything that handles credentials needs write:sensitive. The MCP server exposes read and control tools (list_storage_destinations, test_storage_destination, list_log_archive_policies, set_log_archive_policy, start_log_archive_dump, list_log_archive_runs, list_archived_logs, get_build_cache, set_build_cache) but deliberately has no tool that accepts bucket credentials.
Next steps
Log archive
Ship container logs to a destination on a schedule.
Backups and storage
Use a destination for database and volume backups.