Catch drift in CI
In this tutorial you’ll add a GitHub Actions workflow that runs conn3x drift every hour. When someone changes a server or a cloud resource by hand, the job fails and GitHub notifies you. drift only reads: it changes nothing on your hosts, in your cloud or in state.
Time: 15 minutes.
Prerequisites
Section titled “Prerequisites”- A Conn3x configuration in a GitHub repository, applied at least once. Any configuration from the earlier tutorials works, except the local container from tutorial 1, which GitHub’s runners can’t reach.
- State in a remote backend that the job can read, such as S3 or Postgres. A
terraform.tfstateon your laptop isn’t visible to CI. - Hosts that GitHub-hosted runners can reach over SSH. For private hosts, use a self-hosted runner on their network, or a bastion.
- A private key the runner can log in with. This tutorial assumes the circuit’s
private_key_fileis~/.ssh/id_ed25519and that host keys are checked against~/.ssh/known_hosts, the default.
Add the secrets
Section titled “Add the secrets”In the repository, open Settings > Secrets and variables > Actions and add:
| Secret | Value |
|---|---|
SSH_PRIVATE_KEY |
The private key the circuit logs in with. |
SSH_KNOWN_HOSTS |
The known_hosts lines for every host and jump host. Copy them from your own ~/.ssh/known_hosts, or collect them with ssh-keyscan and check the fingerprints. |
CONN3X_HMAC_KEY |
The content of ~/.config/conn3x/hmac.key from the machine that applied. Only needed if state holds write-only content. |
Add your backend’s credentials as secrets too, and a secret for each variable without a default, passed as TF_VAR_<name>. drift never prompts, so a missing variable is an error.
Add the workflow
Section titled “Add the workflow”Create .github/workflows/drift.yml:
name: Drift
on: schedule: - cron: "17 * * * *" workflow_dispatch:
permissions: contents: read
jobs: drift: runs-on: ubuntu-24.04 env: CONN3X_HMAC_KEY: ${{ secrets.CONN3X_HMAC_KEY }} TF_IN_AUTOMATION: "1" # Your backend's credentials and any TF_VAR_ values go here too. steps: - uses: actions/checkout@v7 with: persist-credentials: false
- name: Install Conn3x env: VERSION: 0.6.3 run: | BASE="https://github.com/conn3xorg/conn3x/releases/download/v${VERSION}" curl -fsSLO "$BASE/conn3x_${VERSION}_linux_amd64.tar.gz" curl -fsSLO "$BASE/checksums.txt" sha256sum --check --ignore-missing checksums.txt tar -xzf "conn3x_${VERSION}_linux_amd64.tar.gz" conn3x sudo install conn3x /usr/local/bin/ conn3x version
- name: Set up SSH env: SSH_PRIVATE_KEY: ${{ secrets.SSH_PRIVATE_KEY }} SSH_KNOWN_HOSTS: ${{ secrets.SSH_KNOWN_HOSTS }} run: | install -d -m 700 ~/.ssh printf '%s\n' "$SSH_PRIVATE_KEY" > ~/.ssh/id_ed25519 chmod 600 ~/.ssh/id_ed25519 printf '%s\n' "$SSH_KNOWN_HOSTS" > ~/.ssh/known_hosts
- run: conn3x init -input=false
- name: Check for drift run: conn3x drift -no-colorThe workflow runs at 17 minutes past every hour, and on demand from the Actions tab through workflow_dispatch. If your configuration isn’t at the root of the repository, point both commands at it with -chdir, as in conn3x -chdir=infra drift -no-color, or set working-directory on the last two steps.
Each step that runs conn3x gets the HMAC key from the job’s env. Without it, Conn3x would make a new key on the runner, and stop at the first write-only file.
What the job reports
Section titled “What the job reports”conn3x drift exits 0 when nothing changed, 2 when something drifted, and 1 on an error, so both drift and errors fail the job. The log shows which resources changed:
Drift found in 1 resource.
Hosts ~ conn3x_file.try__motd["box"] changed
Run conn3x plan to see what Conn3x would change to put them back.When nothing changed:
No drift: the cloud and the hosts match the last apply.drift reports which objects changed, not how. Run conn3x plan on your own machine to see the diff, then either conn3x apply to put things back, or change the configuration to keep the hand edit. The next scheduled run comes back clean.
Run the workflow once by hand from the Actions tab to check the secrets before you rely on the schedule.
Clean up
Section titled “Clean up”To stop the checks, delete .github/workflows/drift.yml, or disable the workflow from the Actions tab. Delete the secrets from Settings > Secrets and variables > Actions if nothing else uses them.
Next steps
Section titled “Next steps”- Find drift covers what to do when drift appears.
- The conn3x drift reference lists its options, such as
-targetto check part of a configuration. - Migrate an Ansible playbook.