Skip to content

Upgrading to 0.8

sat edited this page Aug 25, 2026 · 12 revisions

Upgrading to 0.8

A topology written for 0.7.x meets several changes at once. This page is what to read before upgrading one, and it covers the whole 0.8 line: the sections marked (0.8.1) arrived in that release, and the rest in 0.8.0. Coming from 0.7.x you want all of them; the CHANGELOG describes each in full.

Generated files move

A file declared with path: /etc/frr/frr.conf is written to r1/etc/frr/frr.conf, not r1/frr.conf. A node's files are laid out by the path they take inside the container.

What to do: nothing in the topology. Anything reading generated files by path has to follow — a script of your own, or a downstream tool such as netroub, which reads a lab's generated files to replay failures on it. See File Output.

Interfaces are named eth0, eth1, ...

The default interface prefix is eth, where it used to be net.

What to do: update any template that names an interface literally. eth is what a Linux container calls its interfaces and the only prefix Kathara accepts, so this is the one name every platform agrees on.

containerlab nodes get no management network by default

Generated nodes carry network-mode: none, so a lab has only the links its topology describes. The management network is now opt-in rather than gone.

What to do: if the lab relied on clab exec, the clab-* container names, or reachability through the management network, put it back:

module_config:
  containerlab:
    management_network: true

and name the data interfaces something other than eth0, which containerlab keeps for itself.

A shared segment reaching across machines is replaced with bridges

A multi-host topology that drew a segment spanning machines now gets one bridge per machine, linked to each other, instead of one link leaving a machine per member on the far side.

What to do: nothing, unless the old shape was wanted: global.aggregate_crossing_links: false.

virtual no longer means "not deployed"

One flag used to answer two questions — is this object there? and does dot2net write its configuration? — and a topology could not answer them separately. In 0.8.0 they are two settings (though see the note at the end: one combination is still out of reach):

  • deploy says what the object is materialised as. On a node, container (the default) · platform · none. On an interface or a connection, link · logical · none.
  • virtual: true says only that this object's own configuration is not written. It means the same thing on every kind of class, and it no longer has any bearing on whether the object is deployed.

What to do: replace virtual: true with deploy: none wherever it meant "this is not deployed" — which is what it meant on a node, and what it usually meant on an interface or a connection.

# 0.7.x                        # 0.8.0
nodeclass:                     nodeclass:
  - name: vrouter                - name: vrouter
    virtual: true                  deploy: none

connectionclass:               connectionclass:
  - name: v                      - name: v
    virtual: true                  deploy: logical   # a tunnel the configuration builds

You do not have to find them by reading. A class that sets virtual: true without also naming a deploy is an error, so a topology written for 0.7.x stops on the first one and tells you what to write instead. The error is a migration aid and is removed in 0.9.0; until then, saying which one you mean is required.

Choose deploy: logical rather than none for a connection that is not wired by the platform but is built by the configuration running on the nodes — a VXLAN overlay or a GRE tunnel. Its endpoints are real devices and still get their configuration; only the wiring is absent.

deploy and virtual can now also be combined, which the single flag could not express: deploy: container with virtual: true is a node that starts like any other and that dot2net writes no configuration into.

One combination is still out of reach. deploy: none withholds the configuration as well, so deployed by nobody, configured by us — writing a configuration file for a real router that dot2net does not start — cannot be said in 0.8.0. It needs a form meaning "it exists, we just do not put it there", and the values above do not have one.

Aggregating a segment now names the layer

{{ .segments_<name> }} becomes {{ .segments_<layer>_<name> }}. The layer was missing from the name, so two layers that used one config name were merged without a word.

What to do: add the layer to the name.

# 0.7.x                              0.8.0
- "{{ .segments_network_entry }}"    - "{{ .segments_ip_network_entry }}"

A template that still names the old parameter fails with map has no entry for key "segments_<name>". That happens while the templates are being rendered — late enough that the message can look unrelated to the upgrade — so it is worth searching your templates for segments_ before you start.

Mistakes that used to be quiet are now errors

What Why
An unknown or duplicate key in the config file A key the config did not know was dropped without a word, which looks exactly like a setting that had no effect
Two file definitions writing the same file Whichever was written last won, and the other's content was gone
A config entry naming both template and sourcefile Which came first was decided in the code and written down nowhere

What to do: run dot2net build and fix what it names. Each message says what is wrong and how to write it instead.

A file: entry for <device>.startup now collides with the Kathara module

Two things share the word startup, and it matters which one you have:

  • the file <device>.startup, which Kathara runs inside the device
  • the hook group startup, whose blocks fill that file

0.7.0 documented declaring the file yourself — a file: entry with name_suffix: .startup and output: root. The Kathara module declares it now, so a topology that still does gets two definitions writing one file, which is an error.

What to do: delete the file: entry. Keep the commands, in a config entry written into the startup group (see A hook is a group below for what that is):

nodeclass:
  - name: router
    config:
      - group: startup
        template:
          - "ip link set lo up"

Those are the commands containerlab puts in exec: and TiNET in cmds:, so they are written once and run on all three. A topology that never loads the Kathara module keeps working either way; the collision appears when the module is added, and dot2net names both definitions.

A hook is a group, not a name (0.8.1)

startup, teardown and the four worker_ hooks are written with group::

    config:
      - group: startup          # was: - name: startup
        template:
          - "ip link set lo up"

The old form still works, with a warning, so nothing breaks on upgrade. Two things become possible that were not: two classes of your own may both write into one hook, and a class of any kind may, an interface class included.

A block that has to run after the platform's own command says so by naming it, rather than by picking a number above zero:

      - group: worker_deploy
        placed:
          after: [worker_deploy]

A block left at priority 0 in a worker_ hook is now refused, because it sits exactly where the platform's command does and nothing says which runs first. If you wrote a worker_ block with no priority, give it placed:.

depends: is worked out from what the template reads (0.8.1)

A template that embeds {{ .self_x }}, or merges self_x with blocks:, has already said it needs x rendered first. Writing depends: [x] as well says it twice; the key still works and is still checked, so nothing breaks, but it can go.

include_self does what it says (0.8.1)

A classmembers: entry iterates the members of the classes it names, and the object doing the referring is one of them. The flag that excludes it has not been read since 0.7, so the referrer has been included whatever you wrote.

The default is false — the referrer is not among its own members. If your topology worked around the old behaviour, take the workaround out. If you want the referrer included, say include_self: true.

The wiki said the default was true. No version has done that.

Smaller things (0.8.1)

  • Blank first and last lines of a gathered block are dropped. They used to become empty lines between blocks — an empty command in a script.
  • platform: on a config entry is gone. It selected which output a config belonged to, from a list that never learned about Kathara, and nothing has read it since modules arrived in 0.7.
  • Writing into a group nothing sorts is an error, where the block used to be generated and dropped.
  • Blocks of equal priority come out in the order their classes are declared. Past ten config templates on one object they did not.

See Also

Clone this wiki locally