> ## Documentation Index
> Fetch the complete documentation index at: https://docs.fjall.io/llms.txt
> Use this file to discover all available pages before exploring further.

# CI/CD Integration

> Deploy Fjall applications to AWS from GitHub Actions, Buildkite, or any CI/CD pipeline using OIDC or static credentials.

<AccordionGroup>
  <Accordion icon="book" title="Pre-Requisites">
    Complete these first:

    * [**Deploy Application**](/deployment/deploy-application) at least once locally
    * AWS credentials configured for your CI environment
  </Accordion>
</AccordionGroup>

***

## Introduction

The Fjall CLI runs in CI/CD pipelines without extra configuration. When it detects a non-interactive environment (no TTY), it switches to plain text output and returns standard exit codes.

Official plugins cover **GitHub Actions** and **Buildkite**. For any other CI system, install and invoke the CLI directly.

***

## GitHub Actions

The `fjall-io/fjall-deploy-action` is a composite action that installs the CLI and runs it with the right flags.

### Minimal Workflow

```yaml theme={null}
name: Deploy
on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - uses: fjall-io/fjall-deploy-action@v1
        with:
          target: api
        env:
          AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
          AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          AWS_REGION: us-east-2
```

### With AWS OIDC (Recommended)

Use GitHub's OIDC provider for keyless authentication. No long-lived secrets to rotate:

```yaml theme={null}
name: Deploy
on:
  push:
    branches: [main]

permissions:
  id-token: write
  contents: read

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: arn:aws:iam::123456789012:role/deploy
          aws-region: us-east-2

      - uses: fjall-io/fjall-deploy-action@v1
        with:
          target: api
          verbose: true
```

### With Fjall OIDC

If your app is registered with Fjall, the CLI auto-detects GitHub's OIDC tokens. Just grant `id-token: write` and set your API key:

```yaml theme={null}
permissions:
  id-token: write
  contents: read

steps:
  - uses: actions/checkout@v4
  - uses: fjall-io/fjall-deploy-action@v1
    with:
      target: api
    env:
      FJALL_API_KEY: ${{ secrets.FJALL_API_KEY }}
```

### Split Infrastructure and Code Deploys

Run infrastructure changes and code deploys as separate jobs for faster iteration:

```yaml theme={null}
jobs:
  infra:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: arn:aws:iam::123456789012:role/deploy
          aws-region: us-east-2
      - uses: fjall-io/fjall-deploy-action@v1
        with:
          target: api
          mode: infra-only

  code:
    needs: infra
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: arn:aws:iam::123456789012:role/deploy
          aws-region: us-east-2
      - uses: fjall-io/fjall-deploy-action@v1
        with:
          target: api
          mode: deploy-only
```

### Action Inputs

| Input               | Required | Default  | Description                                        |
| ------------------- | -------- | -------- | -------------------------------------------------- |
| `command`           | no       | `deploy` | `deploy`, `destroy`, or `build`                    |
| `target`            | **yes**  | --       | App name, `organisation`, `platform`, or `account` |
| `service`           | no       | --       | Specific ECS service name                          |
| `mode`              | no       | `full`   | `full`, `infra-only`, or `deploy-only`             |
| `environment`       | no       | --       | Free-form CI/CD label passed to CDK synthesis      |
| `verbose`           | no       | `false`  | Enable verbose logging                             |
| `skip-build`        | no       | `false`  | Skip Docker build                                  |
| `no-cascade`        | no       | `false`  | Skip cascade after organisation deploy             |
| `cli-version`       | no       | `latest` | Pin `fjall` version                                |
| `working-directory` | no       | `.`      | Directory containing `fjall-config.json`           |
| `force`             | no       | `false`  | Force flag for destroy                             |

<Note>
  The `environment` input is a free-form label for your own CI/CD logic. It does not select an AWS account or region. The deploy account is chosen by the active deployment target (`fjall target set`), and the region by your AWS credentials or the `region` setting.
</Note>

***

## Buildkite

The `fjall-io/fjall-deploy-buildkite-plugin` installs the CLI and runs it with the right flags.

### Minimal Pipeline

```yaml theme={null}
steps:
  - label: ":rocket: Deploy"
    plugins:
      - fjall-io/fjall-deploy#v1.0.0:
          target: api
```

### With AWS OIDC

```yaml theme={null}
steps:
  - label: ":rocket: Deploy"
    plugins:
      - aws-assume-role-with-web-identity#v1.0.0:
          role-arn: arn:aws:iam::123456789012:role/deploy
      - fjall-io/fjall-deploy#v1.0.0:
          target: api
```

### Staging to Production Pipeline

```yaml theme={null}
steps:
  - label: ":test_tube: Staging"
    plugins:
      - fjall-io/fjall-deploy#v1.0.0:
          target: api
          environment: staging

  - wait

  - block: ":raised_hand: Deploy to Production?"

  - label: ":rocket: Production"
    plugins:
      - fjall-io/fjall-deploy#v1.0.0:
          target: api
          environment: production
          skip-build: true
```

### Plugin Properties

| Property            | Required | Default  | Description                                        |
| ------------------- | -------- | -------- | -------------------------------------------------- |
| `command`           | no       | `deploy` | `deploy`, `destroy`, or `build`                    |
| `target`            | **yes**  | --       | App name, `organisation`, `platform`, or `account` |
| `service`           | no       | --       | Specific ECS service name                          |
| `mode`              | no       | `full`   | `full`, `infra-only`, or `deploy-only`             |
| `environment`       | no       | --       | Free-form CI/CD label passed to CDK synthesis      |
| `verbose`           | no       | `false`  | Enable verbose logging                             |
| `skip-build`        | no       | `false`  | Skip Docker build                                  |
| `no-cascade`        | no       | `false`  | Skip cascade after organisation deploy             |
| `cli-version`       | no       | `latest` | Pin `fjall` version                                |
| `working-directory` | no       | `.`      | Directory containing `fjall-config.json`           |
| `force`             | no       | `false`  | Force flag for destroy                             |

***

## Raw CLI Usage (Any CI System)

If you're using a different CI system (GitLab CI, CircleCI, Jenkins, etc.), install and invoke the CLI directly.

### Setup

```bash theme={null}
# Install Node.js 22+ and the CLI
npm install -g @fjall/cli

# Deploy
fjall deploy api --non-interactive --skip-confirmation
```

The `--non-interactive` flag switches to plain text output. The `--skip-confirmation` flag suppresses interactive confirmation prompts. Both are essential for CI.

### Key Flags

| Flag                      | Description                                                             |
| ------------------------- | ----------------------------------------------------------------------- |
| `--non-interactive`       | Plain text output (no TUI)                                              |
| `--skip-confirmation`     | Skip confirmation prompts                                               |
| `--infra-only`            | Deploy only infrastructure                                              |
| `--deploy-only`           | Deploy only code                                                        |
| `--skip-build`            | Skip Docker build (use with `--deploy-only`)                            |
| `--skip-migrations`       | Skip database migrations during this deployment                         |
| `-v, --verbose`           | Enable verbose logging                                                  |
| `-e, --environment <env>` | Free-form CI/CD label passed to CDK synthesis (not an account selector) |
| `--target <name>`         | Override the active deployment target for this deploy                   |
| `--region <region>`       | Deploy to a specific region within the target's account                 |
| `--no-cascade`            | Skip cascade for organisation deploys                                   |
| `-f, --force`             | Redeploy all stacks even when no changes are detected                   |

### Exit Codes

| Code  | Meaning              |
| ----- | -------------------- |
| `0`   | Success              |
| `1`   | Failure              |
| `130` | Interrupted (SIGINT) |

### Environment Variables

| Variable                | Description                            |
| ----------------------- | -------------------------------------- |
| `FJALL_API_KEY`         | API key for Fjall OIDC authentication  |
| `AWS_ACCESS_KEY_ID`     | AWS access key                         |
| `AWS_SECRET_ACCESS_KEY` | AWS secret key                         |
| `AWS_REGION`            | AWS region                             |
| `AWS_SESSION_TOKEN`     | Temporary session token (if using STS) |

### Example: GitLab CI

```yaml theme={null}
deploy:
  image: node:22
  script:
    - npm install -g @fjall/cli
    - fjall deploy api --non-interactive --skip-confirmation --verbose
  only:
    - main
```

### Example: CircleCI

```yaml theme={null}
jobs:
  deploy:
    docker:
      - image: cimg/node:22.0
    steps:
      - checkout
      - run: npm install -g @fjall/cli
      - run: fjall deploy api --non-interactive --skip-confirmation --verbose
```

***

## Setting Up AWS Credentials

Whichever CI system you use, the Fjall CLI needs AWS credentials to deploy. Here are the common approaches, from most to least recommended:

### OIDC (Recommended)

Most CI systems support OIDC federation with AWS. This avoids storing long-lived secrets:

1. Create an IAM OIDC identity provider for your CI system
2. Create an IAM role with a trust policy scoped to your repo/pipeline
3. Use your CI's OIDC integration to assume that role

### Static Credentials

Store `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY` as CI secrets. Simpler to set up but requires key rotation.

### Instance Profiles

When your CI agents run on EC2 (for example, self-hosted Buildkite agents), attach an IAM instance profile with the required permissions. No credential management needed.

## Next Steps

<CardGroup cols={2}>
  <Card title="Deploy an Application" icon="rocket" href="/deployment/deploy-application">
    Run a full deploy locally before wiring it into CI/CD.
  </Card>

  <Card title="deploy Command Reference" icon="terminal" href="/cli/deploy">
    Every `fjall deploy` flag, target, and option.
  </Card>

  <Card title="Understanding Profiles" icon="users" href="/deployment/understanding-profiles">
    How Fjall derives AWS profiles and selects the deploy target.
  </Card>

  <Card title="Configure a Deployment User" icon="key" href="/deployment/configure-deployment-user">
    Scope the IAM role your pipeline assumes.
  </Card>
</CardGroup>
