Send, receive, and shield emails with PostScale. One API, EU-hosted. PostScale

    Automation

    GitHub Actions DNS Workflows

    Practical GitHub Actions patterns for DNSControl and Terraform DNS changes, including pull-request previews, protected applies, secrets, and drift checks.

    Updated

    TL;DR

    A good GitHub Actions DNS workflow previews changes on pull requests and applies only after merge to a protected branch. Keep write credentials out of untrusted PR contexts, use environment approvals for production, and run a scheduled drift check so manual DNS edits do not survive unnoticed.

    What you'll learn

    • Build a pull-request DNS preview workflow
    • Apply DNS changes safely from protected branches
    • Store DNS credentials correctly in GitHub Actions
    • Add scheduled drift detection

    GitHub Actions is a good fit for DNS changes if the workflow has a hard boundary:

    pull request = validate and preview
    main branch  = apply

    Do not let every branch mutate production DNS.

    Repository Layout

    A simple DNS-as-code repository can look like this:

    dns/
      dnsconfig.js          # DNSControl
      creds.example.json
      zones/
        example.com.js
     
    terraform/
      dns/
        main.tf
        variables.tf
        versions.tf

    Pick one main path. Mixing Terraform, DNSControl, dashboard edits, and scripts for the same zone creates drift.

    DNSControl Preview on Pull Requests

    This workflow checks DNSControl config and previews the diff.

    name: dnscontrol-preview
     
    on:
      pull_request:
        paths:
          - "dns/**"
          - ".github/workflows/dnscontrol-preview.yml"
     
    jobs:
      preview:
        runs-on: ubuntu-latest
        permissions:
          contents: read
          pull-requests: read
        steps:
          - uses: actions/checkout@v4
     
          - name: Install DNSControl
            run: |
              curl -L https://github.com/StackExchange/dnscontrol/releases/latest/download/dnscontrol-Linux -o /usr/local/bin/dnscontrol
              chmod +x /usr/local/bin/dnscontrol
     
          - name: Check DNS config
            working-directory: dns
            run: dnscontrol check
     
          - name: Preview DNS changes
            working-directory: dns
            env:
              DNSCALE_API_KEY: ${{ secrets.DNSCALE_READ_TOKEN }}
            run: dnscontrol preview

    Use a read-capable token for preview where possible. If your provider requires write-scoped credentials even for preview, do not run this workflow on untrusted forked pull requests.

    DNSControl Apply After Merge

    Apply only from main.

    name: dnscontrol-apply
     
    on:
      push:
        branches: ["main"]
        paths:
          - "dns/**"
     
    jobs:
      apply:
        runs-on: ubuntu-latest
        environment: production
        permissions:
          contents: read
        steps:
          - uses: actions/checkout@v4
     
          - name: Install DNSControl
            run: |
              curl -L https://github.com/StackExchange/dnscontrol/releases/latest/download/dnscontrol-Linux -o /usr/local/bin/dnscontrol
              chmod +x /usr/local/bin/dnscontrol
     
          - name: Apply DNS changes
            working-directory: dns
            env:
              DNSCALE_API_KEY: ${{ secrets.DNSCALE_PRODUCTION_TOKEN }}
            run: dnscontrol push

    Use GitHub environment protection for production so a human approval is required before the job receives production secrets.

    Terraform Plan on Pull Requests

    For Terraform-managed DNS:

    name: terraform-dns-plan
     
    on:
      pull_request:
        paths:
          - "terraform/dns/**"
     
    jobs:
      plan:
        runs-on: ubuntu-latest
        permissions:
          contents: read
          pull-requests: read
        defaults:
          run:
            working-directory: terraform/dns
        steps:
          - uses: actions/checkout@v4
     
          - uses: hashicorp/setup-terraform@v3
     
          - name: Terraform fmt
            run: terraform fmt -check
     
          - name: Terraform init
            run: terraform init -input=false
     
          - name: Terraform validate
            run: terraform validate
     
          - name: Terraform plan
            env:
              DNSCALE_API_KEY: ${{ secrets.DNSCALE_READ_TOKEN }}
            run: terraform plan -input=false

    If your plan needs access to production state, keep that state access out of untrusted PRs.

    Terraform Apply After Merge

    name: terraform-dns-apply
     
    on:
      push:
        branches: ["main"]
        paths:
          - "terraform/dns/**"
     
    jobs:
      apply:
        runs-on: ubuntu-latest
        environment: production
        defaults:
          run:
            working-directory: terraform/dns
        steps:
          - uses: actions/checkout@v4
     
          - uses: hashicorp/setup-terraform@v3
     
          - name: Terraform init
            run: terraform init -input=false
     
          - name: Terraform apply
            env:
              DNSCALE_API_KEY: ${{ secrets.DNSCALE_PRODUCTION_TOKEN }}
            run: terraform apply -auto-approve -input=false

    For higher-risk zones, remove -auto-approve and use an environment approval gate before apply.

    Scheduled Drift Check

    Drift checks catch manual dashboard edits.

    name: dns-drift-check
     
    on:
      schedule:
        - cron: "17 6 * * *"
      workflow_dispatch:
     
    jobs:
      drift:
        runs-on: ubuntu-latest
        steps:
          - uses: actions/checkout@v4
     
          - name: Install DNSControl
            run: |
              curl -L https://github.com/StackExchange/dnscontrol/releases/latest/download/dnscontrol-Linux -o /usr/local/bin/dnscontrol
              chmod +x /usr/local/bin/dnscontrol
     
          - name: Preview drift
            working-directory: dns
            env:
              DNSCALE_API_KEY: ${{ secrets.DNSCALE_READ_TOKEN }}
            run: dnscontrol preview

    Turn this into an alert if the tool exits differently when drift exists. The exact exit-code behavior depends on the tool and provider.

    Secret Rules

    Use separate secrets:

    DNSCALE_READ_TOKEN
    DNSCALE_STAGING_TOKEN
    DNSCALE_PRODUCTION_TOKEN

    Prefer:

    • zone-scoped tokens
    • separate staging and production zones
    • no production secrets in forked PRs
    • GitHub environment approval for production
    • regular rotation

    Review Checklist

    Before approving a DNS PR:

    1. Does the preview match the description?
    2. Are deleted records intentional?
    3. Are MX, SPF, DKIM, DMARC, CAA, and DS changes reviewed by the right owner?
    4. Are TTL changes planned around cache behavior?
    5. Is rollback obvious?
    6. Does this change affect certificate issuance or email delivery?

    Frequently asked questions

    Should GitHub Actions apply DNS changes from pull requests?
    No. Pull requests should run validation and preview. Apply should run only after merge to a protected branch, ideally behind a production environment approval.
    Can I expose DNS secrets to forked pull requests?
    No. Treat forked pull requests as untrusted. Do not run write-token workflows in that context.
    Where do I put the DNScale API key?
    Store it as a GitHub Actions secret or in a secret manager. Prefer zone-scoped API keys and separate production from staging.
    Should I use Terraform or DNSControl in GitHub Actions?
    Use Terraform when DNS is part of broader infrastructure state. Use DNSControl when you want DNS-specific diffs and multi-provider DNS workflows.
    How often should drift checks run?
    Daily is enough for many teams. Run more often for production zones where manual emergency edits happen.
    How do I roll back a DNS change?
    Revert the commit and let the apply workflow push the previous record state. For migrations, prepare rollback before the change window.

    Ready to manage your DNS with confidence?

    DNScale provides anycast DNS hosting with a global network, real-time analytics, and an easy-to-use API.

    Start free