Automated Nix Flake Updates

Published Dec 7, 2025 Revision: 1 · map

Nix and its ecosystem has become one of my computing obsessions. This year I've built up a flake with outputs for many of my devices spanning Linux, MacOS, and Android. I find myself running nix flake update daily. Most of the time changes have no conflict, but breaking changes or build failures can slip under the radar if they conflict with my Android configuration while I update and build on Linux.

To remove my personal need to update the lockfile of my dotfiles I turned to GitHub actions. Actions can be run on multiple platforms for free on public projects. This allows me to verify that my flake builds under multiple hardware and software configurations.

Updating the flake

The first step for this workflow is updating my flake.lock file. This can be done manually with the nix flake update command, but for actions I found DeterminateSystems' purpose-built update-flake-lock action.

name: Update Flakeon:  schedule:    - cron: "0 0 * * *"  workflow_dispatch:jobs:  update-flake:    runs-on: ubuntu-latest    permissions:      contents: write      pull-requests: write    steps:      - name: Checkout repository        uses: actions/checkout@v4      - name: Setup Nix        uses: ./.github/actions/setup-nix      - name: Update flake.lock        uses: DeterminateSystems/update-flake-lock@v28        with:          token: ${{ secrets.GH_TOKEN_FOR_UPDATES }}          pr-title: "chore: update flake.lock"          pr-labels: automated          pr-body: |            Automated flake dependency update.            {{env.GIT_COMMIT_MESSAGE}}

An important detail here is GH_TOKEN_FOR_UPDATES. The default author for automated pull requests is Github actions bot. When Github actions bot opens a Pull Request automated checks are not run. To get automated checks I've created a PAT (Personal Access Token) to open the pull request with my identity.

This action runs daily, opening a Pull Request labeled automated with the updated flake.lock.

In this pull request we can see that my inputs for ghostty, home manager, nixpkgs, and nixpkgs unstable were all updated.

Automated Merge

With the pull request open we can now take advantage of automated checks to gatekeep these changes. We only want to merge if the updated flake inputs can build without error on each targeted platform. To verify these builds I will use nix build with the --dry-run flag. This validates the configuration as if we were building it, but does not build all of the packages. Since all we are looking for is verification of the build here this is perfect.

jobs:  check-x86-linux:    name: Check x86_64-linux configs    runs-on: ubuntu-latest    strategy:      matrix:        config: [desktop, eink, pocket]    steps:      - name: Checkout code        uses: actions/checkout@v4      - name: Setup Nix        uses: ./.github/actions/setup-nix      - name: Build ${{ matrix.config }} configuration        run:          nix build .#nixosConfigurations.${{ matrix.config          }}.config.system.build.toplevel --dry-run

I made a short action to setup nix. Once in the shell nix build can be run directly. This is all I need for each platform, this example is running on ubuntu-latest. Checking other systems is as easy as changing runs-on to macos-latest and ubuntu-24.04-arm.

check-darwin:  name: Check aarch64-darwin config  runs-on: macos-latest  steps:    - name: Checkout code      uses: actions/checkout@v4    - name: Setup Nix      uses: ./.github/actions/setup-nix    - name: Build macbook configuration      run: nix build .#darwinConfigurations.macbook.system --dry-run

My complete pull request action can be viewed here. The last component to complete this automation workflow is an auto merge configuration. Here I use the automated label that was set by the update-flake-lock action to choose which PRs to auto merge.

auto-merge:  needs: [check-x86-linux, check-arm-linux, check-darwin]  runs-on: ubuntu-latest  if: contains(github.event.pull_request.labels.*.name, 'automated')  permissions:    contents: write    pull-requests: write  steps:    - name: Enable auto-merge      run: gh pr merge --auto --squash "$PR_URL"      env:        PR_URL: ${{ github.event.pull_request.html_url }}        GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}

I set out to automate away my need to run nix flake update and this workflow has succeeded! Now if I think about updating I will run a git pull instead to see if changes came in last night. This has also been doubly useful for exposing when my configuration is broken for a different machine than the one I edited it from. I'll have to mute this notification eventually, but for now I enjoy receiving an email in the evening letting me know another daily update has passed successfully.