Configuration Management#
Containerlab's prime task is to enable its user with a simple and intuitive interface to build, manage and share networking labs. And one of the important aspects of any lab is the configuration of its nodes.
Today, Containerlab offers the following ways to configure your lab nodes:
-
Startup configuration file
The most straight forward way to configure your nodes is to use the startup configuration file, that can be templated either prior the deployment phase, or during it.The config can be provided in the full canonical configuration format or by providing a file with commands that will be run on top of the default configuration.
-
Ansible
Thanks to the auto-generated Ansible inventory, you can use any Ansible playbook to configure your nodes post deployment. -
Nornir
Containerlab also generates a simple Nornir YAML inventory, that can be used to configure your nodes post deployment.
Startup configuration file#
It is possible to provide the startup configuration that the node applies on boot for most Containerlab kinds. The startup config can be provided in two ways:
- As a path to a file that is available on the host machine and contains the config blob that the node understands.
- As an embedded config blob that is provided as a multiline string in YAML.
- As a remote file using a URL that points to a file with the config blob.
Local#
When a path to a startup-config file is provided, containerlab either mounts the file to the container by a path that NOS expects to have its startup-config file, or it will apply the config via using the NOS-dependent method.
Check the particular kind documentation to see if the startup-config is supported and how it is applied.
Startup-config path variable
By default, the startup-config references are either provided as an absolute or a relative (to the current working dir) path to the file to be used.
Consider a two-node lab mylab.clab.yml
with seed configs that the user may wish to use in their lab. A user could create a directory for such files similar to this:
.
├── cfgs
│ ├── node1.partial.cfg
│ └── node2.partial.cfg
└── mylab.clab.yml
2 directories, 3 files
Then to leverage these configs, the node could be configured with startup-config references like this:
name: mylab
topology:
nodes:
node1:
startup-config: cfgs/node1.partial.cfg
node2:
startup-config: cfgs/node2.partial.cfg
while this configuration is correct, it might be considered verbose as the number of nodes grows. To remove this verbosity, the users can use a special variable __clabNodeName__
in their startup-config paths. This variable will expand to the node-name for the parent node that the startup-config reference falls under.
name: mylab
topology:
nodes:
node1:
startup-config: cfg/__clabNodeName__.partial.cfg
node2:
startup-config: cfgs/__clabNodeName__.partial.cfg
The __clabNodeName__
variable can also be used in the kind and default sections of the config. Using the same directory structure from the example above, the following shows how to use the magic variable for a kind.
name: mylab
topology:
defaults:
kind: nokia_srlinux
kinds:
nokia_srlinux:
startup-config: cfgs/__clabNodeName__.partial.cfg
nodes:
node1:
node2:
The following example shows how one would do it using defaults.
Embedded#
It is possible to embed the startup configuration in the topology file itself. This is done by providing the startup-config as a multiline string.
Note
If a config file exists in the lab directory for a given node, then it will take preference over the startup config passed with this setting. If it is desired to discard the previously saved config and use the startup config instead, use the enforce-startup-config
setting or deploy a lab with the reconfigure
flag.
Remote#
It is possible to specify a remote http(s)
location for a startup-config file. Simply provide a URL that can be accessed from the containerlab host.
topology:
kinds:
nokia_srlinux:
type: ixrd3
image: ghcr.io/nokia/srlinux
startup-config: https://raw.githubusercontent.com/srl-labs/containerlab/main/tests/02-basic-srl/srl2-startup.cli
The remote file will be downloaded to the containerlab's temp directory at $TMP/.clab/<filename>
path and provided to the node as a locally available startup-config file. The filename will have a generated name that follows the pattern <lab-name>-<node-name>-<filename-from-url>
, where <filename-from-url>
is the last element of the URL path.
Note
- Upon deletion of a lab, the downloaded startup-config files will not be removed. A manual cleanup should be performed if required.
- If a lab is redeployed with the lab name and startup-config paths unchanged, the local file will be overwritten.
- For https locations the certificates won't be verified to allow fetching artifacts from servers with self-signed certificates.
Customisation options#
While many labs would be just fine with providing the partial or full configs to the lab nodes, some advanced labs might want to customize the startup config file before providing it to the node.
Containerlab offers a few options to customize the startup config - env vars expansion and templating.
Env vars expansion#
Every startup configuration file that is provided to a lab node undergoes environment variable expansion procedure. This means you can embed environment variables in your startup config file and they will be expanded at runtime.
Both env vars defined in the Containerlab topology file and available in the host environment will be expanded. You can use advanced environment variable syntax to support default values, as shown in the Environment variables documentation.
Here is an example of a lab that makes use of env vars expansion:
If you deploy this lab with SRL_LOCATION
env var set to some value, like shown below, you will have this value set to the provided value (or defaulted to default-value
if the env var is not set).
Templating#
Env vars expansion is useful when your parametrization tasks are simple and limited to direct string substitution.
For more advanced config management tasks, you can use templating. Containerlab uses Go text/template1 package for templating and users can provide a template as a startup config that will be rendered at runtime.
The templating happens after env vars expansion, so you can use env vars in your template and templating together.
The templating engine receive the whole Node object as a template context. The Node object is everything you have defined under the .topology.nodes.<node-name>
with the defaults and kinds merged in.
Here is an example how you can access values defined in your node definition from within the template:
Here we are referring to the Image
field of the node object that is defined in the NodeDefinition struct.
Deploying this topology will result in the location
string to return:
docker exec clab-srl-srl1 sr_cli info system information
location "I have been deployed from ghcr.io/nokia/srlinux:25.3.1"
As you can see, the templating engine accessed the Image
field of the NodeDefinition struct and replaced it with the actual image name provided in the topology file.
This was just a simple example to introduce you to the templating engine and the way it accesses templating data. Now let's see how you can make it work on a more complex example.
You can provide arbitrary variables data to your lab nodes by using the .topology.nodes.<node-name>.config.vars
field, for example:
name: srl
topology:
nodes:
srl1:
kind: nokia_srlinux
image: ghcr.io/nokia/srlinux:25.3.1
config:
vars:
ifaces:
- 2
- 4
In the topology file above we defined a variable called ifaces
that contains a list of interfaces to enable on the SR Linux node. Now we can create a template for the startup config that will enable the interfaces defined in the ifaces
variable.
{{- $ifaces := index .Config.Vars "ifaces" }}
{{- if $ifaces }}
{{- range $iface := $ifaces }}
set / interface ethernet-1/{{ $iface }} description "Interface {{ $iface }} configured by template"
{{- end }}
{{- end }}
With this template in place, deploy the lab and check the descriptions of the interfaces we configured via the template:
docker exec clab-srl-srl1 sr_cli info interface ethernet-1/\{2,4\} description
interface ethernet-1/2 {
description "Interface 2 configured by template"
}
interface ethernet-1/4 {
description "Interface 4 configured by template"
}
Note, that the NodeDefinition
structure only defines .Config.Vars
field, therefore to access any nested data structures you need to use the index
function. Like in the example above where we access the ifaces
list by using index .Config.Vars "ifaces"
.
Functions#
Go text/template has built-in functions you can use in your template such as range
, index
and so on.
On top of the built-in function, Containerlab bundles the following custom functions:
strings.Split
- splits a string by a delimiter. Documented here.strings.ReplaceAll
- replaces all occurrences of a substring with a new substring. Documented here.conv.Join
- joins a list of strings into a single string. Documented here.conv.ToInt
- converts a string to an integer. Documented here.
If a function you need is not available, check if it is available in the gomplate documentation and ask a request to add it via Containerlab's issue tracker.
-
You can use https://repeatit.io/ to play online with Go templating language. ↩