Skip to main content

Command Palette

Search for a command to run...

Version Control for Network Configurations

Git and GitLab CI/CD for Network Teams

Published
9 min read
Version Control for Network Configurations

Network configurations change constantly — interfaces go up and down, routes shift, software gets upgraded. Without version control, there's no record of what changed, when, or by whom. Git addresses this for code. Here's how to extend that approach to your entire network.

Why version control for networks?

Software developers have relied on version control for decades, tracking every line of code, attributing every change, and allowing mistakes to be undone with a single command. Network engineers, however, have traditionally lacked this convenience—configurations reside on devices, changes are made manually, and the audit trail depends on what you remember to document.

Applying Git to network configuration management changes this completely. When every config change is committed to a repository you get a full history of your network state, the ability to diff any two points in time, and the foundation for automation via CI/CD pipelines.

Git fundamentals for network engineers

Git is a distributed version control system where each repository copy holds the complete history. You work locally, commit changes, and then push to a shared server, such as GitLab.

Git Terminology

Before moving forward, let's ensure we have a clear understanding of some key terminology.

Repository : In Git, a repository refers to the database that holds all of a project's information, including files, metadata, and history. Here, "project" is used to denote any arbitrary collection of work

Working directory : This is the directory where you, as a Git user, modify the files within the repository.

Index : The index represents the repository’s directory structure and contents at a specific moment. It is a dynamic binary file managed by Git, which updates as you stage changes and commit them to the repository.

Commit : A commit is an entry in the Git repository that records metadata for each change made. This metadata includes the author, the date of the commit, and a commit message, which describes the change introduced to the repository.

Essential Git Commands

Command Detail
git init Start a new git repository in the current folder
git clone Download a copy of a GitLab repository to your machine
git status Show which files have changed since the last commit
git add . Stage all changed files — prepare them for a commit
git commit -m "msg" Save a named snapshot of all staged changes
git push origin main Upload your commits to the remote GitLab repository
git pull origin main Download and merge the latest changes from GitLab
git log --oneline Show a compact history of all commits in the repo
git diff Show exactly what lines changed in unstaged files
git reset --hard Discard all local changes and return to last commit

Here's the core workflow for managing network configuration files:

# Make a change to your router list
vim inventory.yml

# Stage the change
git add inventory.yml

# Save a named snapshot
git commit -m "Add router3 to lab inventory"

# Upload to GitLab — triggers pipeline automatically
git push origin main

Automating Nokia SROS Backups with GitLab CI/CD and Python

What if your router configurations backed themselves up every night and flagged exactly what changed? Here is how I built a fully automated backup and inventory pipeline for Nokia SROS routers using GitLab CI/CD and Netmiko.

The problem

Manual router backups are tasks everyone knows they should perform but often neglect. You might make a change late on a Friday, forget to save a copy, and weeks later find yourself looking at a configuration that seems unfamiliar, with no clue when or how it was altered.

Managing a lab environment running Nokia SR OS routers brought this problem into sharp focus. The goal was simple: automate both the backup and change detection so that nothing slips through the cracks, and store everything in version control so you always have a full audit trail.

The solution

GitLab CI/CD pipelines

GitLab is a web-based platform that hosts Git repositories and adds powerful automation on top. The .gitlab-ci.yml file in your repository defines a pipeline — a sequence of automated jobs that run whenever specific events occur.

#.gitlab-ci.yml snapshot 

backup-job: # job name
  stage: backup # which stage it belongs to
  variables:
    GIT_STRATEGY: fetch # how to get the repo code
  script:
    - python backup.py # your automation script
    - git add clab-backups/
    - git commit -m "router backup [ci skip]"   
    - git push origin HEAD:$CI_COMMIT_BRANCH -o ci.skip
  tags:
    - nokia-runner # which machine runs this

Repository Structure

💡
Note : The lab environment is enabled by Containerlab

What this pipeline does ?

This is a two-job GitLab CI/CD pipeline that runs on a schedule. One job gathers router inventory and identifies changes, while the other captures the complete configuration. All results are committed back to the same GitLab repository, providing a version-controlled history of your entire network state.

inventory-job — smart change detection

Connects to each router via SSH using Netmiko, executes show commands, and compares the output with the last saved report. A new file is created and committed to the repository only if changes are detected.

Smart saving: if nothing changed between runs the job exits cleanly with no commit and no push. Your git history only grows when something actually happened on the network.

backup-job — full config capture

Always runs on every pipeline. Captures the complete running configuration via admin display-config. Saves a timestamped file per router. Verifies completeness by checking for exit all at the end. Keeps the last three backups per router.

The complete pipeline file

image: python:3.9.21

variables:
  PIP_CACHE_DIR: "$CI_PROJECT_DIR/.cache/pip"
  GIT_STRATEGY: fetch

cache:
  paths:
    - .cache/pip
    - venv/

stages:
  - inventory
  - backup

before_script:
  - python -V
  - python -m pip install --upgrade pip
  - python -m pip install virtualenv
  - python -m virtualenv venv
  - source venv/bin/activate
  - pip install -r requirements.txt
  - rm -fr .git/rebase-merge .git/rebase-apply
  - git config --global user.email "$GITLAB_USER_EMAIL"
  - git config --global user.name "$GITLAB_USER_ID"
  - git config --global pull.rebase false
  - git fetch origin
  - git reset --hard origin/$CI_COMMIT_BRANCH

inventory-job:
  stage: inventory
  variables:
    GIT_STRATEGY: fetch
  script:
    - python inventory.py
    - git add clab-inventory/
    - >
      if ! git diff-index --quiet HEAD; then
        git commit -m "inventory update [ci skip]"
        git remote set-url --push origin "https://\(TOKEN_NAME:\)ACCESS_TOKEN@\(CI_SERVER_HOST/\)CI_PROJECT_PATH.git"
        git push origin HEAD:$CI_COMMIT_BRANCH -o ci.skip
      else
        echo "No inventory changes to commit"
      fi
  rules:
    - if: '$CI_PIPELINE_SOURCE == "push"'
      changes:
        - inventory.yml
    - if: '$CI_PIPELINE_SOURCE == "web"'
    - if: '$CI_PIPELINE_SOURCE == "schedule"'
  tags:
    - nokia-runner

backup-job:
  stage: backup
  variables:
    GIT_STRATEGY: fetch
  script:
    - python backup.py
    - git add clab-backups/
    - >
      if ! git diff-index --quiet HEAD; then
        git commit -m "router backup [ci skip]"
        git remote set-url --push origin "https://\(TOKEN_NAME:\)ACCESS_TOKEN@\(CI_SERVER_HOST/\)CI_PROJECT_PATH.git"
        git push origin HEAD:$CI_COMMIT_BRANCH -o ci.skip
      else
        echo "No backup changes to commit"
      fi
  tags:
    - nokia-runner

Expected Output

💡
When both jobs are successfully executed

inventory-job log output

Created output files

Set it up from scratch

  1. Create Gitlab Project - Log into gitlab.com, click New Project, create a blank private project named router-backups.

  2. Add your files - Create .gitlab-ci.yml, backup.py, inventory.py, inventory.yml, and requirements.txt in the repository root.

  3. Set CI/CD variables - Go to Settings > CI/CD > Variables. Add ACCESS_TOKEN (Maintainer role), TOKEN_NAME, and ROUTER_PASSWORD (masked).

  4. Register a GitLab Runner - Install gitlab-runner on the Linux machine that can reach your routers. Register with shell executor and tag nokia-runner.

  5. Set up a schedule - Go to Build > Schedules, create a schedule with cron 0 2 * * * to run daily at 2 AM.

  6. Run the first pipeline - Go to Build > Pipelines, click Run pipeline. Both jobs execute and first backup files appear in clab-backups/.

Once the runner is registered and variables are set, the system is self-maintaining. It backs up your routers and records changes automatically — no manual steps needed.

Key lessons learned

  • Use shell executor, not docker : Docker networking prevents the runner from reaching internal lab routers. Shell executor runs directly on the machine and has full network access to your SROS devices.

  • environment no more is not optional : Nokia SR OS paging will silently truncate your backups. This must be the first command you send after connecting — before any show commands or admin display-config.

  • Hard reset beats rebase in CI/CD : git reset --hard origin/main at the start of every job is more reliable than trying to rebase or merge diverged branches mid-pipeline.

  • Project Access Token needs Maintainer role : A token with Guest role cannot push to the repo. Create a Project Access Token and explicitly set the role to Maintainer during creation.

  • Filenames matter — check for hidden spaces : A leading space in inventory.yml caused hours of confusion. Always run ls -la on the runner to verify filenames have no invisible characters.

  • send_command_timing for large output : For admin display-config, timing-based reading is more reliable than pattern matching on large outputs that take unpredictable time to stream from the router.

💡
The pipeline now runs daily at 2 AM, backs up two Nokia SROS routers, and commits only when something actually changed — fully hands-free.