Skip to main content

Overview

fjall build builds Docker images and pushes them to Amazon ECR. Use it in CI/CD pipelines that separate the build step from the deploy step, so you can test the exact image you are about to ship. The command is non-interactive by design. It prints a three-step progress stream and, with --output-image-url, a machine-readable block your pipeline can parse.

Prerequisites

Usage

Arguments

Options

--target on fjall build is the Docker build target stage. --target on fjall deploy is the deployment target (account and region). Same flag name, two different meanings.

Agent flags

These flags shape output for AI-agent and scripted callers.

Basic usage

Build every service in an application:
Build one service:
Build a named Dockerfile stage:

Build arguments

--build-arg bakes a public value into the image at build time. It is repeatable, and it is the highest tier of the resolution ladder:
Two rules govern which keys reach the build:
  • A key is emitted when your infrastructure.ts declares it, when you pass it via --build-arg, or when a key prefixed VITE_, NEXT_PUBLIC_ or PUBLIC_ is found in a parsed .env file.
  • Shell environment variables override the value of an already-declared key. They never introduce a new key, so arbitrary host environment cannot leak into the image.
An empty string at any tier counts as absent and falls through to the next tier.
A build arg is baked into the image layer and readable by anyone who can pull it. Use --build-secret for anything private.

Build secrets

--build-secret passes a value to the build through BuildKit’s secret mount, so it never lands in an image layer. It is repeatable, and each token is a comma-separated list of key=value pairs: an id plus exactly one source.
Mount it in your Dockerfile:
Give each reference exactly one source: ssm, secretsManager or env. A malformed token fails the whole command before any AWS work starts, so a typo never reaches a build silently.

CI/CD workflow

The build command suits pipelines that:
  1. Build and push the image.
  2. Run tests against the pushed image.
  3. Deploy that exact image without rebuilding.
fjall ci run build <target> [service] wraps this command with a resolved deployment target and CI deploy-token credentials. See fjall ci and the CI/CD integration guide.

Example: GitHub Actions

This is a workflow excerpt, not a complete pipeline. Each job runs on a fresh runner: install the CLI, check out the project where needed, supply a scoped CI deploy token, and authenticate Docker to ECR before pulling the private test image. Select the deployment target explicitly in the real pipeline. The extraction below is suitable only when the intended services share one build group; reject empty or multiple image selections before deploying.
--image-tag deploys the image the build step pushed, skipping the build and implying --deploy-only. It applies one tag family across the application’s services, so it fits applications whose services build from a single build group. For applications with independent build groups, deploy normally with fjall deploy api.

Example: Buildkite

This excerpt assumes an agent with the CLI, project checkout, deploy credentials and ECR Docker authentication already configured. Apply the same image-selection and explicit-target checks as above.

Output

Standard output

The Deploy this build line appears only when every built artefact shares one digest. When services build from separate build groups, the last line reads Deploy: fjall deploy api instead. Image tags follow <service>-sha-<12 hex characters>, with a -<target> segment folded in for a multi-stage service. The tag identifies the service and the content of the build, not the git ref, so there is no --tag flag to override it.

Machine-readable output

Pass --output-image-url to print a parseable block:
Parse the per-service IMAGE lines when you need a specific service’s tag. IMAGE_URL is the first artefact, not a summary of the build.

Docker build targets

If your Dockerfile uses multi-stage builds with named targets:
Build a specific stage:

Troubleshooting

ECR repository not found

Cause: the application’s Network stack has never been deployed, so the ECR repository and its CloudFormation export do not exist. The export name is the PascalCase form of the application name plus EcrRepositoryName. Fix: deploy the infrastructure once.

Docker build failed

Cause: a Dockerfile error, a missing dependency, or an unmounted build secret. Fix: reproduce the build locally, then re-run.

AWS account ID unavailable

Cause: before it builds anything, fjall build asks AWS STS which account its AWS credentials belong to (GetCallerIdentity), because it pushes to that account’s ECR registry. The call succeeded, but the answer named no account. AWS STS names the account whenever that call succeeds, so something else answered it. The AWS SDK inside the CLI sends STS calls to the endpoint that AWS_ENDPOINT_URL_STS or AWS_ENDPOINT_URL names, or that an endpoint_url in your AWS config file names, whenever one is set. Credentials that STS refuses stop the build with STS’s own error instead, so signing in again, to Fjall or to AWS, does not change this answer. Confirming a fjall login in this shell, or forcing one, would also replace the saved login for every shell and agent session that reads this state root. Fix: take the override away for the build. Unset the variable that sets it, or put AWS_IGNORE_CONFIGURED_ENDPOINT_URLS=true in front of the command: the SDK then ignores every configured endpoint, in the environment and the config file alike, for that one command.
When both AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY are set, the CLI uses them in place of the credentials it derives for the target (see Environment credentials override derivation). If they are keys for the endpoint, such as a local AWS emulator’s, rather than for the account you deploy to, unset them as well: unset AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY AWS_SESSION_TOKEN. With no override set, something on the network path answered in AWS STS’s place, and the build needs a route to AWS STS itself.

Wrong organisation

Cause: the organisation binding in fjall-config.json does not match the organisation your credential belongs to. The gate refuses only on a proven mismatch, and it runs before the first AWS call. Fix: authenticate as the bound organisation, or, if the project really is moving, rebind it by name. The fjall login the refusal names signs in again on the state root this shell already reads. That replaces the saved login for every shell and agent session that reads the root, whether you answer Yes at the overwrite prompt or pass --force. Where anything else is working on the machine, sign in to the bound organisation in a state root of its own instead:
On fjall login, --org takes the bound organisation’s id from the refusal, so the device flow signs you in to that organisation. The refusal’s other cure, FJALL_API_KEY set to a key for the bound organisation, also reaches it from one shell alone, because the CLI reads that variable before any stored credential. To keep the new root for later sessions, in a shell profile or a project’s agent settings, see Choosing an organisation.

Next steps

Deploy an application

Ship the image you just pushed

fjall deploy

Full reference for the deploy command, including --image-tag

fjall ci

Run builds and deploys from a CI pipeline

CI/CD integration

Pipeline templates, tokens, and exit codes