Documentation

Configuration

New
Browse documentation

Published 2026-08-13. V2 is a prerelease; the web core is release-gated and other previews are named separately. What beta means.

Read this page as HTML, Markdown, or structured JSON—or open the published Markdown with an AI assistant. Gemini receives the prompt through your clipboard because its signed-out page does not reliably prefill URL text; paste when the new tab opens. External assistants need the public site URL.

1.4.1
Unavailable in this version
1.5
Unavailable in this version

Asset Pipeline configuration

Supported web path: This is the asset contract generated by Amber CLI 2.0.6 for Amber 2.0.0-beta.5 and asset_pipeline 0.37.0.

Complete Asset Pipeline first. Every filesystem path below is resolved from the application root, beside shard.yml.

Keep build-time and runtime responsibilities separate

File: config/assets.cr — this complete generated file configures only the runtime manifest resolver.

Crystal
Amber::Assets.configure(
  manifest_path: "public/assets/manifest.json"
)

Do not require the compiler from config/assets.cr. That file is loaded into the running server, which only needs Amber's resolver. Keeping the compiler in the CLI or a build script prevents release tooling from becoming an accidental runtime dependency.

The three paths have different owners:

Setting Value Owner
source_root app/assets source files developers edit
output_root public/assets generated release files
public_path /assets URLs emitted into the manifest

Never point source_root and output_root at the same directory. Never store uploads in either directory. The build is allowed to replace generated output; it must not delete application source or runtime data.

Nested logical paths are preserved.

Reference structure — authored source files:

Output
app/assets/stylesheets/app.css
app/assets/javascript/controllers/menu.js
app/assets/images/marketing/hero.webp
app/assets/fonts/Manrope-Variable.woff2

remain distinct logical entries with the same relative paths, even though the emitted filenames include content digests. Directory preservation prevents two files such as admin/logo.svg and store/logo.svg from colliding.

File: scripts/build_assets.cr — an existing pre-2.0.5 application can use this complete build-only wrapper.

Crystal
require "asset_pipeline/static_assets"

AssetPipeline::StaticAssets::Compiler.new(
  source_root: Path["app/assets"],
  output_root: Path["public/assets"],
  public_path: "/assets"
).build

Configure Amber's resolver

File: src/my_app.cr — verify the application entry point loads top-level configuration before controllers and models.

Crystal
require "../config/*"

Replace my_app with the target name. The generated V2 entry point uses that wildcard, so config/assets.cr is loaded. If a migrated app has a narrower require list, add require "../config/assets" explicitly after the configuration file that loads Amber. Do not put the setup in the empty config/initializers/ directory unless the app explicitly requires it.

The resolver is strict for logical paths. A missing entry is a build or deploy failure to fix, not a reason to fall back silently to an unhashed URL. External URLs, absolute application paths, fragments, and data: URLs pass through.

Inspect the manifest directly

Application views should normally use Amber's helpers. Build tooling can load the same manifest directly when it needs structured metadata.

File: a build verification program, for example scripts/verify_assets.cr — create this complete file.

Crystal
require "asset_pipeline/static_assets"

manifest = AssetPipeline::StaticAssets::Manifest.load(
  Path["public/assets/manifest.json"]
)
manifest.verify(Path["public/assets"])

puts manifest.path("stylesheets/app.css")
puts manifest.integrity("stylesheets/app.css")
entry = manifest.entry("fonts/Manrope-Variable.woff2")
puts "#{entry.content_type} #{entry.bytes} bytes"

Each entry records its public path, full SHA-256 digest, SRI value, content type, and byte count. verify checks those values against the emitted bytes and deterministic gzip companions. path, integrity, and entry are strict lookups; a miss stops release verification.

Run from: the application root, after building assets.

Terminal
crystal run scripts/verify_assets.cr

CSS references

File: app/assets/stylesheets/app.css — use paths relative to this source stylesheet for local authored files.

CSS
@font-face {
  font-family: "Manrope";
  src: url("../fonts/Manrope-Variable.woff2") format("woff2");
  font-display: swap;
}

.hero {
  background-image: url("../images/marketing/hero.webp");
}

The build rewrites those local references to the fingerprinted public paths. Keep an external URL, root-absolute URL, fragment, or data URL only when that is deliberately outside the manifest. A missing relative file is an error.

Relative CSS @import references are rewritten too. Query strings and fragments on a local reference are preserved after the fingerprinted path. The compiler also rewrites relative static imports, exports, dynamic imports, and source-map references in browser-ready JavaScript. Bare module names stay unchanged so an import map can resolve them.

Asset Pipeline copies the bytes supplied to it. Generate real responsive image sizes and formats in an earlier deterministic build step if the application needs them, then put every emitted variant under app/assets/images/ and list the real logical paths in srcset or <picture>. Query parameters such as ?w=640 or ?format=webp do not create an image variant.

Development workflow

Rebuild assets after an authored source file changes, then let the Amber watcher reload application code.

Run from: the application root.

Terminal
amber assets build
amber watch

amber watch already runs the same compiler before application compilation when app/assets/**/* changes. Running amber assets build explicitly is useful before the initial watcher start and when diagnosing output. The compiler is never a first-request hook.

Production workflow

Run from: the application root — build before compiling or packaging the application.

Terminal
shards install --production
amber assets build
amber assets check
crystal spec
shards build my_app --release

For an older app that uses scripts/build_assets.cr, run that file instead of amber assets build, then run scripts/verify_assets.cr. Both paths invoke the same asset_pipeline 0.37.0 manifest contract.

Package bin/my_app, config/, and the complete generated public/assets/ tree. Start the runtime with a read-only release directory. A writable public/assets/ path or a warm-up request must never be required.

The compiler writes files atomically, publishes manifest.json last, and after a successful rebuild removes stale files owned by the previous manifest. It does not delete unrelated files under public/assets/. Deployment still must copy or switch the complete generated tree as one unit.

Deploy atomically: place a complete release in a new directory, verify it, then switch traffic. Rollback switches back to the prior complete directory. Do not copy new files over an old asset tree, and do not share a manifest between releases.

Cache boundary

Reference response header — apply only to fingerprinted asset URLs:

Output
Cache-Control: public, max-age=31536000, immutable

HTML and public/assets/manifest.json must revalidate or use a short cache so clients can discover a new deployment. Unfingerprinted aliases must never be cached as immutable. Configure compression in Amber's static handler or the reverse proxy, and verify Content-Type, Content-Encoding, and Vary rather than assuming a CDN corrected them.

Release verification

Verify at least one CSS file, JavaScript module, image, font, and other binary:

  1. build assets from a clean checkout;
  2. load manifest.json and perform strict lookups;
  3. start the compiled app with the release directory read-only;
  4. request every emitted URL and check bytes and content type;
  5. confirm fingerprinted responses receive immutable caching;
  6. confirm HTML and the manifest do not;
  7. edit each source class, rebuild, and confirm its URL changes; and
  8. switch back to the prior complete release and confirm its URLs still work.