{"title":"Manual Binary Deployment","description":"Compile an Amber V2 application and run it as an unprivileged Linux service","section":"deployment","version":"v2","path":"deployment/manual-deploy","canonical_url":"https://amberframework.org/docs/v2/deployment/manual-deploy","markdown_url":"https://amberframework.org/docs/v2/deployment/manual-deploy.md","inherited":false,"content_markdown":"# Manual Binary Deployment\n\nThis example keeps compilation and runtime responsibilities explicit. Adjust\npaths, the service user, and the target name for your application.\n\n## Build the release artifact\n\nAmber CLI `2.0.6` builds and checks application assets after installing shards\nand before compiling the binary. These are build-time commands; the running web\nprocess only reads the finished manifest and files.\n\n```bash\nshards install --production\namber assets build\namber assets check\ncrystal spec\nshards build my_app --release\nfile bin/my_app\n```\n\nFor an existing app using the explicit migration wrapper, replace the two\n`amber assets` lines with `crystal run scripts/build_assets.cr` and its manifest\nverification. Neither path may start the HTTP process or make a warm-up request\nto create release files.\n\nCopy `bin/my_app`, `config/`, and the built `public/` artifact to one new release\ndirectory. If your application reads other files at runtime, include them\ndeliberately. Do not copy development secrets or a local database.\n\nUser uploads are runtime data, not release assets. Keep them on a persistent\nmounted volume or in object storage and leave them out of the directory replaced\nby each deployment. Back up local uploads independently. The application release\nmay be read-only after its authored assets have been built.\n\nFor a manifest-enabled application, confirm the release contains both\n`public/assets/manifest.json` and every fingerprinted file it names. Include the\ndeterministic `.gz` companions. Do not copy only files that changed; a release\ndirectory is a complete unit.\n\n**File: `config/environments/production.yml` — configure only the fallback for\nunfingerprinted files; Amber applies immutable caching to fingerprinted names.**\n\n```yaml\nstatic:\n  headers:\n    Cache-Control: \"no-cache\"\n```\n\n## Configure the process\n\nStore secrets in the host or deployment platform's secret manager. A minimal\nenvironment is:\n\n```bash\nAMBER_ENV=production\nAMBER_SERVER_HOST=0.0.0.0\nAMBER_SERVER_PORT=3000\nAMBER_SERVER_SECRET_KEY_BASE=replace-with-a-long-random-secret\n```\n\n`AMBER_SERVER_PORT` overrides `server.port` from\n`config/environments/production.yml`. Add `DATABASE_URL` only when the\napplication has a configured database adapter.\n\n## Example systemd unit\n\n```ini\n[Unit]\nDescription=my_app Amber service\nAfter=network.target\n\n[Service]\nType=simple\nUser=my_app\nGroup=my_app\nWorkingDirectory=/srv/my_app\nEnvironmentFile=/etc/my_app.env\nExecStart=/srv/my_app/bin/my_app\nRestart=on-failure\nRestartSec=3\nNoNewPrivileges=true\n\n[Install]\nWantedBy=multi-user.target\n```\n\nThe environment file should be readable only by the service administrator and\nservice account. Terminate TLS in a reverse proxy or managed ingress and proxy\nto `127.0.0.1:3000` when the proxy runs on the same host.\n\n## Verify before shifting traffic\n\n```bash\ncurl --fail --show-error http://127.0.0.1:3000/\n```\n\nConfirm the expected page, logs, restart behavior, and any persistence or file\nstorage dependencies before sending production traffic. Keep each binary,\nconfiguration, generated asset manifest, and generated public assets together\nas one immutable release. Roll back by switching traffic to the complete prior\nrelease; never combine an older manifest with newer asset files.\n\nFor a manifest-enabled release, inspect the rendered HTML, copy one CSS,\nJavaScript, image, font, and binary URL, and request each directly. Verify:\n\n- the response body matches the built file;\n- CSS and local JavaScript dependencies point at existing fingerprinted URLs;\n- `Content-Type` matches the manifest, including `font/woff2`, `image/avif`,\n  `application/wasm`, and other deployed formats;\n- fingerprinted URLs return\n  `Cache-Control: public, max-age=31536000, immutable`;\n- HTML and `manifest.json` do not receive immutable caching;\n- gzip clients receive valid compressed bytes with the original media type and\n  `Vary: Accept-Encoding`; and\n- conditional requests and byte ranges still work.\n\nKeep the prior release directory until its HTML can no longer send clients to\nits asset URLs. A database rollback is a separate decision: do not reverse a\nnon-backward-compatible migration merely because the binary or assets roll back."}