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

# How to deploy with GitHub Actions

Set up a GitHub Actions workflow that deploys your Intuned project every time you merge to `main`. The workflow makes your Git repository the source of truth: each deploy replaces the project's code on Intuned with the code on `main`.

<Warning>
  Each deploy overwrites the project on Intuned. Changes made only on Intuned,
  such as edits in the [Online IDE](/docs/main/02-features/online-ide) or fixes from
  [self-healing](/docs/main/02-intuned-agent/self-healing-projects), are lost on the
  next deploy. To keep a change made on Intuned, pull it into your repository
  with [`intuned dev checkout`](/docs/main/05-references/cli/dev#checkout) and commit
  it before the next merge.
</Warning>

## Before you start

* Your Intuned project is at the root of a GitHub repository.
* Your Intuned settings file (`Intuned.json` or `Intuned.jsonc`) sets `projectName` and `workspaceId`. See the [Intuned.json reference](/docs/main/05-references/intuned-json).
* `.intuned/` is listed in your `.gitignore`. The CLI writes local checkout information there, and the workflow relies on it not being committed.
* You have an Intuned API key. See [how to manage API keys](/docs/main/03-how-to/manage/manage-api-keys).

## Set up the workflow

### 1. Add your API key to GitHub

In your GitHub repository, go to **Settings** > **Secrets and variables** > **Actions** and create a repository secret named `INTUNED_API_KEY` with your API key as the value.

### 2. Add the workflow file

Create `.github/workflows/deploy-intuned.yml`:

<CodeGroup>
  ```yaml TypeScript icon="github" theme={null}
  name: Deploy to Intuned

  on:
    push:
      branches: [main]
    workflow_dispatch:

  permissions:
    contents: read

  env:
    INTUNED_CLI_VERSION: 0.1.19

  jobs:
    deploy:
      if: github.ref == 'refs/heads/main'
      runs-on: ubuntu-latest
      concurrency:
        group: intuned-deploy
        cancel-in-progress: false
      steps:
        - uses: actions/checkout@v7
        - name: Set up Node
          uses: actions/setup-node@v7
          with:
            node-version: 24
        - name: Install Intuned CLI
          run: npm install -g "@intuned/cli@$INTUNED_CLI_VERSION"
        - name: Enable Corepack
          run: corepack enable
        - name: Install dependencies
          run: yarn install --frozen-lockfile
        - name: Typecheck
          run: npx tsc --noEmit
        - name: Deploy
          env:
            INTUNED_API_KEY: ${{ secrets.INTUNED_API_KEY }}
          run: intuned dev deploy --non-interactive --on-conflict override
  ```

  ```yaml Python icon="github" theme={null}
  name: Deploy to Intuned

  on:
    push:
      branches: [main]
    workflow_dispatch:

  permissions:
    contents: read

  env:
    INTUNED_CLI_VERSION: 0.1.19

  jobs:
    deploy:
      if: github.ref == 'refs/heads/main'
      runs-on: ubuntu-latest
      concurrency:
        group: intuned-deploy
        cancel-in-progress: false
      steps:
        - uses: actions/checkout@v7
        - name: Set up Node
          uses: actions/setup-node@v7
          with:
            node-version: 24
        - name: Install Intuned CLI
          run: npm install -g "@intuned/cli@$INTUNED_CLI_VERSION"
        - name: Set up Python
          uses: actions/setup-python@v7
          with:
            python-version: "3.12"
        - name: Install uv
          run: pip install uv
        - name: Install dependencies
          run: uv sync --locked
        - name: Deploy
          env:
            INTUNED_API_KEY: ${{ secrets.INTUNED_API_KEY }}
          run: intuned dev deploy --non-interactive --on-conflict override
  ```
</CodeGroup>

What each part does:

* **Triggers**: the workflow runs on every push to `main`, and you can run it manually from the **Actions** tab. The `if: github.ref == 'refs/heads/main'` check stops a manual run from deploying any other branch.
* **`concurrency`**: only one deploy runs at a time. If several merges land while a deploy is running, GitHub keeps only the newest waiting run, so the latest commit on `main` is the one that ends up deployed.
* **`INTUNED_CLI_VERSION`**: pins the CLI so a new release can't change your pipeline unannounced. Replace `0.1.19` with the [latest version on npm](https://www.npmjs.com/package/@intuned/cli), and bump it deliberately.
* **Install dependencies**: `yarn install --frozen-lockfile` and `uv sync --locked` fail if your lockfile is out of date with `package.json` or `pyproject.toml`, so dependency problems stop the job before anything is deployed. If your TypeScript project uses npm instead of Yarn, replace the Corepack and install steps with `npm ci`.
* **Typecheck** (TypeScript): a type error fails the job before anything is deployed.
* **Deploy**: `--non-interactive` turns off prompts, and `--on-conflict override` makes the commit replace the project on Intuned. See [How the deploy works](#how-the-deploy-works).

Add any other checks your project needs, such as linting or tests, before the **Deploy** step. A failing step stops the deploy.

<Note>
  If your settings file doesn't set `workspaceId`, add a repository variable
  named `INTUNED_WORKSPACE_ID` and pass it to the **Deploy** step with
  `INTUNED_WORKSPACE_ID: ${{ vars.INTUNED_WORKSPACE_ID }}`. See [CLI
  configuration](/docs/main/05-references/cli/configuration).
</Note>

### 3. Run the first deploy

Merge the workflow file to `main`, or open the **Actions** tab, select **Deploy to Intuned**, and select **Run workflow**. The **Deploy** step logs the files it saved and the deployment result. If the project doesn't exist on Intuned yet, the first deploy creates it.

## How the deploy works

`intuned dev deploy` saves the files to the project on Intuned, then starts a deployment.

* **The save replaces the project's code.** The CLI normally compares your files with the version you checked out, which it records in `.intuned/checkout`. That file isn't in the repository, so the workflow has no checkout to compare against. With `--on-conflict override`, the save replaces the project's code with the files in the commit, and files on Intuned that aren't in the commit are removed.
* **Ignored files aren't uploaded.** The CLI skips files matched by your `.gitignore` and its default exclusions, such as `node_modules/`, `.env`, and `.intuned/`.
* **The job waits for the deployment.** The **Deploy** step fails if the deployment fails, or if it's still pending after 10 minutes.
* **Jobs defined in code deploy with the project.** Files in `intuned-resources/jobs/` create, update, and delete Jobs on each deploy. Jobs created in the dashboard or through the API aren't affected. See [Jobs](/docs/main/02-features/jobs-batched-executions).
* **Environment variables aren't part of the deploy.** Keep the values your APIs read at runtime on Intuned. See [environment variables and secrets](/docs/main/02-features/environment-variables-secrets).

## Troubleshooting

| Error in the **Deploy** step | Fix |
| - | - |
| `Checkout information is required to save this project` | Add `--on-conflict override` to the deploy command. |
| `The local checkout is stale because the project has changed on Intuned` | `.intuned/checkout` is committed to the repository. Remove it with `git rm -r --cached .intuned`, add `.intuned/` to `.gitignore`, and commit. |
| `The agent is still creating this project` | Finish the Intuned Agent session that is creating the project, then re-run the workflow. |
| `Deployment failed: ...` | Open the project on Intuned to see the deployment error, fix it in the repository, and merge the fix. |
| `Deployment timed out after 10 minutes` | Check the deployment status on Intuned. If it finished, no action is needed. Otherwise, re-run the workflow. |


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.