Version Control for Network Configurations
Git and GitLab CI/CD for Network Teams

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
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
inventory-job log output
Created output files
Set it up from scratch
Create Gitlab Project - Log into gitlab.com, click New Project, create a blank private project named router-backups.
Add your files - Create .gitlab-ci.yml, backup.py, inventory.py, inventory.yml, and requirements.txt in the repository root.
Set CI/CD variables - Go to Settings > CI/CD > Variables. Add ACCESS_TOKEN (Maintainer role), TOKEN_NAME, and ROUTER_PASSWORD (masked).
Register a GitLab Runner - Install gitlab-runner on the Linux machine that can reach your routers. Register with shell executor and tag nokia-runner.
Set up a schedule - Go to Build > Schedules, create a schedule with cron 0 2 * * * to run daily at 2 AM.
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 commandsoradmin 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.ymlcaused 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.



