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

# Deploy an artifact to S3

> Set up a GitHub Actions workflow to upload Lambda artifacts to the environment S3 bucket.

When you create an environment, Gapstack automatically creates two resources in **your** AWS account and region:

1. An **S3 bucket** for artifacts
2. An **OpenID Connect (OIDC) IAM role** so GitHub Actions can authenticate to AWS without long-lived access keys

Copy the bucket name and role from the environment **Details** page. Store those values as GitHub Actions variables, then add the following steps to your workflow.

<Note>
  Do not store AWS access keys in GitHub. Use the OIDC role from the environment.
</Note>

<Steps>
  <Step title="Copy values from the environment Details page">
    Open the environment in Gapstack and click **Details**. Copy these fields:

    * **OpenID Role** → `ROLE_TO_ASSUME`
    * **Artifacts** → `BUCKET_SOURCE`
    * **Region** → `AWS_REGION`
    * The environment name → `ENVIRONMENT` and `STAGE`

    Use the copy icon next to each field.

    <Frame caption="Copy OpenID Role and Artifacts from the environment Details page.">
      <img src="https://mintcdn.com/gapstack/sZs3EiNge1VOl78f/images/environment-bootstrap.png?fit=max&auto=format&n=sZs3EiNge1VOl78f&q=85&s=3aa43e8ec57ff3453e8f4223054c480e" alt="Environment Details page with OpenID Role and Artifacts highlighted. Each field has a copy button." width="5176" height="2962" data-path="images/environment-bootstrap.png" />
    </Frame>
  </Step>

  <Step title="Add GitHub Actions variables">
    Add the values you copied as repository variables so the workflow can read them.

    1. In GitHub, open the repository.
    2. Click **Settings**.
    3. Click **Secrets and variables**, then **Actions**.
    4. Open the **Variables** tab.
    5. Click **New repository variable** for each variable below.

    | Name             | Value                                                                |
    | ---------------- | -------------------------------------------------------------------- |
    | `AWS_REGION`     | **Region** on the environment Details page (for example `us-west-1`) |
    | `ENVIRONMENT`    | The environment name (for example `prod`)                            |
    | `STAGE`          | The stage name (for example `prod`)                                  |
    | `BUCKET_SOURCE`  | **Artifacts** on the environment Details page                        |
    | `ROLE_TO_ASSUME` | **OpenID Role** on the environment Details page                      |
  </Step>

  <Step title="Set environment variables">
    Add an `env` block at the top of the workflow. It reads the repository variables you created.

    ```yaml theme={null}
    env:
      STAGE: ${{ vars.STAGE }}
      APP_NAME: ${{ github.event.repository.name }}
      AWS_REGION: ${{ vars.AWS_REGION }}
      ENVIRONMENT: ${{ vars.ENVIRONMENT }}
      BUCKET_SOURCE: ${{ vars.BUCKET_SOURCE }}
      ROLE_TO_ASSUME: ${{ vars.ROLE_TO_ASSUME }}
    ```
  </Step>

  <Step title="Configure AWS credentials (OIDC)">
    Assume the environment OIDC role. GitHub issues a short-lived token. AWS trusts that token.

    ```yaml theme={null}
    - name: Configure AWS credentials
      uses: aws-actions/configure-aws-credentials@v4
      with:
        role-to-assume: ${{ env.ROLE_TO_ASSUME }}
        role-session-name: OIDCSession
        aws-region: ${{ env.AWS_REGION }}
    ```

    <Info>
      The job needs `permissions: id-token: write` so GitHub can mint the OIDC token.
    </Info>
  </Step>

  <Step title="Upload Artifact to S3">
    Copy the zip into the environment artifacts bucket. Change `./cmd/main.zip` if your build output lives somewhere else.

    ```yaml theme={null}
    - name: Upload Artifact
      run: |
        aws s3 cp ./cmd/main.zip s3://${{ env.BUCKET_SOURCE }}/${{ env.APP_NAME }}/${{ env.ENVIRONMENT }}/main.zip
    ```
  </Step>
</Steps>

## Required GitHub Actions variables

| Variable         | Copy from                                          |
| ---------------- | -------------------------------------------------- |
| `ROLE_TO_ASSUME` | **OpenID Role** on the environment Details page    |
| `AWS_REGION`     | **Region** on the environment Details page         |
| `BUCKET_SOURCE`  | **Artifacts** on the environment Details page      |
| `ENVIRONMENT`    | The environment name (for example `dev` or `prod`) |
| `STAGE`          | The stage name (for example `prod`)                |

## Example workflow

```yaml theme={null}
name: Upload artifact

on:
  push:
    branches: [main]

permissions:
  id-token: write
  contents: read

env:
  STAGE: ${{ vars.STAGE }}
  APP_NAME: ${{ github.event.repository.name }}
  AWS_REGION: ${{ vars.AWS_REGION }}
  ENVIRONMENT: ${{ vars.ENVIRONMENT }}
  BUCKET_SOURCE: ${{ vars.BUCKET_SOURCE }}
  ROLE_TO_ASSUME: ${{ vars.ROLE_TO_ASSUME }}

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

      - name: Configure AWS credentials
        uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: ${{ env.ROLE_TO_ASSUME }}
          role-session-name: OIDCSession
          aws-region: ${{ env.AWS_REGION }}

      - name: Upload Artifact
        run: |
          aws s3 cp ./cmd/main.zip s3://${{ env.BUCKET_SOURCE }}/${{ env.APP_NAME }}/${{ env.ENVIRONMENT }}/main.zip
```

`APP_NAME` comes from the repository name. The other values come from the GitHub Actions variables you added.

## Next steps

After the file is in S3, create a Lambda and select that artifact. See [Deploy a Lambda](/guides/deploy-lambda).
