Skip to content

Templates and Inheritance

Thomas Mangin edited this page Aug 1, 2026 · 8 revisions

Templates and Inheritance

Configuration reuse patterns for large-scale ExaBGP deployments


Table of Contents


Overview

Templates and inheritance allow you to define configuration once and reuse it across multiple neighbors, reducing duplication and maintenance burden.

Key concepts:

  • Template: A named configuration block that can be inherited
  • Inheritance: A neighbor inherits settings from a template using inherit
  • Group: A collection of neighbors with shared configuration
  • Override: Child configurations can override template settings

Benefits:

  • DRY: Don't Repeat Yourself - define once, reuse everywhere
  • Consistency: Ensure all neighbors use same settings
  • Maintainability: Update one template, affect all inheritors
  • Scalability: Manage hundreds of neighbors efficiently

Why Use Templates

Without Templates (Repetitive)

neighbor 192.168.1.1 {
    router-id 10.0.0.1;
    local-address 192.168.1.2;
    local-as 65001;
    peer-as 65000;

    family {
        ipv4 unicast;
        ipv6 unicast;
    }

    capability {
        route-refresh;
        graceful-restart;
    }
}

neighbor 192.168.2.1 {
    router-id 10.0.0.1;
    local-address 192.168.2.2;
    local-as 65001;
    peer-as 65000;

    family {
        ipv4 unicast;
        ipv6 unicast;
    }

    capability {
        route-refresh;
        graceful-restart;
    }
}

neighbor 192.168.3.1 {
    router-id 10.0.0.1;
    local-address 192.168.3.2;
    local-as 65001;
    peer-as 65000;

    family {
        ipv4 unicast;
        ipv6 unicast;
    }

    capability {
        route-refresh;
        graceful-restart;
    }
}

Problems:

  • 30+ lines for 3 neighbors
  • Changing capabilities requires editing 3 places
  • Error-prone (typos, inconsistencies)
  • Doesn't scale to 100+ neighbors

With Templates (DRY)

# Define template once
template {
    neighbor ebgp-peer {
        router-id 10.0.0.1;
        local-as 65001;

        family {
            ipv4 unicast;
            ipv6 unicast;
        }

        capability {
            route-refresh;
            graceful-restart;
        }
    }
}

# Use template (just 5 lines per neighbor)
neighbor 192.168.1.1 {
    inherit ebgp-peer;
    local-address 192.168.1.2;
    peer-as 65000;
}

neighbor 192.168.2.1 {
    inherit ebgp-peer;
    local-address 192.168.2.2;
    peer-as 65000;
}

neighbor 192.168.3.1 {
    inherit ebgp-peer;
    local-address 192.168.3.2;
    peer-as 65000;
}

Benefits:

  • 18 lines total (vs 30+)
  • Change capabilities once, affects all neighbors
  • Consistent configuration guaranteed
  • Easy to add 100 more neighbors

Template Syntax

Basic Template Definition

template {
    neighbor <template-name> {
        <directives>
    }
}

Example:

template {
    neighbor basic-peer {
        router-id 10.0.0.1;
        local-as 65001;

        family {
            ipv4 unicast;
        }

        capability {
            route-refresh;
        }
    }
}

Multiple Templates

You can define multiple templates:

template {
    # Template for eBGP peers
    neighbor ebgp-peer {
        router-id 10.0.0.1;
        local-as 65001;

        family {
            ipv4 unicast;
            ipv6 unicast;
        }

        capability {
            route-refresh;
            graceful-restart;
        }
    }

    # Template for iBGP peers
    neighbor ibgp-peer {
        router-id 10.0.0.1;
        local-as 65001;
        peer-as 65001;  # iBGP

        family {
            ipv4 unicast;
            ipv6 unicast;
            ipv4 mpls-vpn;
        }

        capability {
            route-refresh;
            graceful-restart 120;
        }
    }

    # Template for FlowSpec
    neighbor flowspec-peer {
        router-id 10.0.0.1;
        local-as 65001;

        family {
            ipv4 flow;
        }
    }
}

Using Templates with inherit

Basic Inheritance

template {
    neighbor common-settings {
        router-id 10.0.0.1;
        local-as 65001;

        family {
            ipv4 unicast;
        }
    }
}

neighbor 192.168.1.1 {
    inherit common-settings;
    local-address 192.168.1.2;
    peer-as 65000;
}

What happens:

  1. Neighbor inherits all directives from template
  2. Neighbor adds/overrides with its own directives
  3. Final configuration combines both

Overriding Template Values

Child configurations can override template settings:

template {
    neighbor default-peer {
        router-id 10.0.0.1;
        local-as 65001;
        hold-time 180;  # Default hold-time

        family {
            ipv4 unicast;
        }
    }
}

neighbor 192.168.1.1 {
    inherit default-peer;
    local-address 192.168.1.2;
    peer-as 65000;
}

neighbor 192.168.2.1 {
    inherit default-peer;
    local-address 192.168.2.2;
    peer-as 65000;
    hold-time 90;  # Override: use shorter hold-time
}

Result:

  • 192.168.1.1 uses hold-time 180 (from template)
  • 192.168.2.1 uses hold-time 90 (overridden)

Adding to Template Configuration

You can add to template configuration:

template {
    neighbor basic-peer {
        router-id 10.0.0.1;
        local-as 65001;

        family {
            ipv4 unicast;
        }

        capability {
            route-refresh;
        }
    }
}

neighbor 192.168.1.1 {
    inherit basic-peer;
    local-address 192.168.1.2;
    peer-as 65000;

    # Add additional family
    family {
        ipv4 unicast;
        ipv6 unicast;  # Added
    }

    # Add MD5 authentication
    md5-password "secret123";
}

Important: Family blocks are merged, not replaced. Other directives are replaced when overridden.


Grouping Neighbors

ExaBGP 3.4 had a group block. It no longer exists: a template plus inherit does the same job, and the same template can be shared by any number of neighbors.

Shared settings for several neighbors

template {
    neighbor internal-peers {
        local-address 10.0.0.1;
        local-as 65001;
        peer-as 65001;  # iBGP

        family {
            ipv4 unicast;
        }

        capability {
            route-refresh;
        }
    }
}

neighbor 10.0.0.2 {
    inherit internal-peers;
    router-id 10.0.0.1;
    description "iBGP Peer 1";
}

neighbor 10.0.0.3 {
    inherit internal-peers;
    router-id 10.0.0.1;
    description "iBGP Peer 2";
}

neighbor 10.0.0.4 {
    inherit internal-peers;
    router-id 10.0.0.1;
    description "iBGP Peer 3";
}

Benefits:

  • The template is inherited by every neighbor which asks for it
  • Each neighbor only carries what makes it different
  • Clear logical grouping

Per-neighbor overrides

template {
    neighbor edge-routers {
        local-as 65001;
        peer-as 65000;  # eBGP

        family {
            ipv4 unicast;
        }

        capability {
            route-refresh;
            graceful-restart;
        }
    }
}

# Neighbor with the template defaults
neighbor 192.168.1.1 {
    inherit edge-routers;
    router-id 10.0.0.1;
    local-address 192.168.1.2;
    description "Edge Router 1";
}

# Neighbor overriding one directive
neighbor 192.168.2.1 {
    inherit edge-routers;
    router-id 10.0.0.1;
    local-address 192.168.2.2;
    description "Edge Router 2 (short hold-time)";
    hold-time 90;  # overrides the template
}

# Neighbor adding one directive
neighbor 192.168.3.1 {
    inherit edge-routers;
    router-id 10.0.0.1;
    local-address 192.168.3.2;
    description "Edge Router 3 (MD5 auth)";
    md5-password "secret123";
}

Several templates

process ddos-blocker {
    run /etc/exabgp/ddos-blocker.py;
    encoder json;
}

template {
    neighbor ibgp-peers {
        local-address 10.0.0.1;
        local-as 65001;
        peer-as 65001;

        outgoing-ttl 5;

        family {
            ipv4 unicast;
            ipv6 unicast;
        }
    }

    neighbor ebgp-peers {
        local-as 65001;

        family {
            ipv4 unicast;
        }

        capability {
            route-refresh;
        }
    }

    neighbor flowspec-peers {
        local-as 65001;

        family {
            ipv4 flow;
        }

        api {
            processes [ ddos-blocker ];
        }
    }
}

neighbor 10.0.0.2 {
    inherit ibgp-peers;
    router-id 10.0.0.1;
}

neighbor 10.0.0.3 {
    inherit ibgp-peers;
    router-id 10.0.0.1;
}

neighbor 192.168.1.1 {
    inherit ebgp-peers;
    router-id 10.0.0.1;
    local-address 192.168.1.2;
    peer-as 65000;
}

neighbor 192.168.2.1 {
    inherit ebgp-peers;
    router-id 10.0.0.1;
    local-address 192.168.2.2;
    peer-as 65002;
}

neighbor 192.168.10.1 {
    inherit flowspec-peers;
    router-id 10.0.0.1;
    local-address 192.168.10.2;
    peer-as 65000;
}

Inheritance Rules

Rule 1: Template Must Be Defined First

Wrong:

neighbor 192.168.1.1 {
    inherit my-template;  # ERROR: template not defined yet
    # ...
}

template {
    neighbor my-template {
        # ...
    }
}

Correct:

template {
    neighbor my-template {
        # ...
    }
}

neighbor 192.168.1.1 {
    inherit my-template;  # OK: template defined above
    # ...
}

Rule 2: Only One Template Per Neighbor

Wrong:

neighbor 192.168.1.1 {
    inherit template1;
    inherit template2;  # ERROR: can't inherit multiple templates
    # ...
}

Workaround: Use nested inheritance or merge templates manually.


Rule 3: Child Overrides Take Precedence

template {
    neighbor my-template {
        hold-time 180;
        md5-password "default";
    }
}

neighbor 192.168.1.1 {
    inherit my-template;
    hold-time 90;           # Overrides template
    md5-password "custom";  # Overrides template
    # Final: hold-time=90, md5-password="custom"
}

Rule 4: Family Blocks Are Merged

template {
    neighbor my-template {
        family {
            ipv4 unicast;
        }
    }
}

neighbor 192.168.1.1 {
    inherit my-template;
    family {
        ipv6 unicast;  # Merged with template
    }
    # Final: ipv4 unicast + ipv6 unicast
}

Note: This is an exception to Rule 3. Most blocks are replaced, but family is merged.


Rule 5: API Processes Are Merged

template {
    neighbor my-template {
        api {
            processes [ process1 ];
        }
    }
}

neighbor 192.168.1.1 {
    inherit my-template;
    api {
        processes [ process2 ];
    }
    # Final: processes [ process1, process2 ]
}

Variables and Substitution

ExaBGP does not have native variable substitution. However, you can achieve this using:

  1. Configuration generation scripts
  2. Environment variables in processes
  3. External templating tools (Jinja2, ERB, etc.)

Method 1: Shell Script Generation

#!/bin/bash

# Variables
ROUTER_ID="10.0.0.1"
LOCAL_AS="65001"
PEER_AS="65000"

# Generate configuration
cat > /etc/exabgp/exabgp.conf <<EOF
template {
    neighbor ebgp-peer {
        router-id ${ROUTER_ID};
        local-as ${LOCAL_AS};
        peer-as ${PEER_AS};

        family {
            ipv4 unicast;
        }
    }
}

neighbor 192.168.1.1 {
    inherit ebgp-peer;
    local-address 192.168.1.2;
}

neighbor 192.168.2.1 {
    inherit ebgp-peer;
    local-address 192.168.2.2;
}
EOF

Method 2: Jinja2 Templates

Template file (exabgp.conf.j2):

template {
    neighbor ebgp-peer {
        router-id {{ router_id }};
        local-as {{ local_as }};
        peer-as {{ peer_as }};

        family {
            ipv4 unicast;
        }
    }
}

{% for neighbor in neighbors %}
neighbor {{ neighbor.ip }} {
    inherit ebgp-peer;
    local-address {{ neighbor.local_address }};
    description "{{ neighbor.description }}";
}
{% endfor %}

Render script (generate_config.py):

from jinja2 import Template

config = {
    'router_id': '10.0.0.1',
    'local_as': 65001,
    'peer_as': 65000,
    'neighbors': [
        {'ip': '192.168.1.1', 'local_address': '192.168.1.2', 'description': 'Router 1'},
        {'ip': '192.168.2.1', 'local_address': '192.168.2.2', 'description': 'Router 2'},
        {'ip': '192.168.3.1', 'local_address': '192.168.3.2', 'description': 'Router 3'},
    ]
}

with open('exabgp.conf.j2') as f:
    template = Template(f.read())

with open('/etc/exabgp/exabgp.conf', 'w') as f:
    f.write(template.render(config))

Method 3: Process Environment Variables

Environment variables can be used inside processes, not in configuration file:

process healthcheck {
    run /etc/exabgp/api/healthcheck.py;
    encoder text;
    env {
        SERVICE_IP = "100.10.0.100";
        SERVICE_PORT = "80";
        ANNOUNCE_PREFIX = "100.10.0.0/24";
    }
}

Process script (healthcheck.py):

import os

service_ip = os.environ['SERVICE_IP']
service_port = os.environ['SERVICE_PORT']
announce_prefix = os.environ['ANNOUNCE_PREFIX']

# Use variables in logic
if check_health(service_ip, service_port):
    print(f"announce route {announce_prefix} next-hop self")
else:
    print(f"withdraw route {announce_prefix}")

DRY Principles

DRY: Don't Repeat Yourself

Principle 1: Extract Common Configuration

Before:

neighbor 192.168.1.1 { router-id 10.0.0.1; local-as 65001; family { ipv4 unicast; } }
neighbor 192.168.2.1 { router-id 10.0.0.1; local-as 65001; family { ipv4 unicast; } }
neighbor 192.168.3.1 { router-id 10.0.0.1; local-as 65001; family { ipv4 unicast; } }

After:

template {
    neighbor common { router-id 10.0.0.1; local-as 65001; family { ipv4 unicast; } }
}
neighbor 192.168.1.1 { inherit common; local-address 192.168.1.2; peer-as 65000; }
neighbor 192.168.2.1 { inherit common; local-address 192.168.2.2; peer-as 65000; }
neighbor 192.168.3.1 { inherit common; local-address 192.168.3.2; peer-as 65000; }

Principle 2: Template Hierarchy

Create specialized templates from base templates (via config generation):

Base template:

template {
    neighbor base-peer {
        router-id 10.0.0.1;
        local-as 65001;

        capability {
            route-refresh;
        }
    }
}

Specialized templates (add features):

template {
    neighbor base-peer { /* ... */ }

    neighbor ebgp-peer {
        router-id 10.0.0.1;
        local-as 65001;

        capability {
            route-refresh;
            graceful-restart;  # Added for eBGP
        }

        family {
            ipv4 unicast;
            ipv6 unicast;
        }
    }

    neighbor ibgp-peer {
        router-id 10.0.0.1;
        local-as 65001;
        peer-as 65001;  # iBGP

        capability {
            route-refresh;
        }

        family {
            ipv4 unicast;
            ipv6 unicast;
            ipv4 mpls-vpn;  # Added for iBGP
        }
    }
}

Principle 3: Single Source of Truth

Bad: Duplicate values across config

neighbor 192.168.1.1 { local-as 65001; /* ... */ }
neighbor 192.168.2.1 { local-as 65001; /* ... */ }
neighbor 192.168.3.1 { local-as 65001; /* ... */ }
# If AS number changes, must update 3 places

Good: Define once in template

template {
    neighbor common { local-as 65001; /* ... */ }
}
neighbor 192.168.1.1 { inherit common; /* ... */ }
neighbor 192.168.2.1 { inherit common; /* ... */ }
neighbor 192.168.3.1 { inherit common; /* ... */ }
# AS number change: update 1 place

Large-Scale Deployment Patterns

Pattern 1: Regional Peering (100+ Neighbors)

# Template for all peers
template {
    neighbor global-peer {
        router-id 10.0.0.1;
        local-as 65001;

        family {
            ipv4 unicast;
            ipv6 unicast;
        }

        capability {
            route-refresh;
            graceful-restart;
        }
    }
}

# US region
neighbor 192.168.1.1 { inherit global-peer; local-address 192.168.1.2; peer-as 65100; description "US-East-1"; }
neighbor 192.168.1.3 { inherit global-peer; local-address 192.168.1.2; peer-as 65101; description "US-East-2"; }
neighbor 192.168.2.1 { inherit global-peer; local-address 192.168.2.2; peer-as 65102; description "US-West-1"; }
# ... 50 more US peers

# EU region
neighbor 192.168.10.1 { inherit global-peer; local-address 192.168.10.2; peer-as 65200; description "EU-West-1"; }
neighbor 192.168.10.3 { inherit global-peer; local-address 192.168.10.2; peer-as 65201; description "EU-West-2"; }
# ... 50 more EU peers

Note: there is no group block, every neighbor names the template it inherits. To avoid repeating the line 50 times, generate the configuration (see the pattern at the end of this page).


Pattern 2: Multi-Service BGP (Different Families)

# Base template
template {
    neighbor base-config {
        router-id 10.0.0.1;
        local-as 65001;
        capability { route-refresh; }
    }
}

# Unicast template
template {
    neighbor unicast-peer {
        router-id 10.0.0.1;
        local-as 65001;
        family { ipv4 unicast; ipv6 unicast; }
        capability { route-refresh; graceful-restart; }
    }
}

# FlowSpec template
template {
    neighbor flowspec-peer {
        router-id 10.0.0.1;
        local-as 65001;
        family { ipv4 flow; }
    }
}

# VPN template
template {
    neighbor vpn-peer {
        router-id 10.0.0.1;
        local-as 65001;
        family { ipv4 mpls-vpn; l2vpn evpn; }
        capability { extended-message; }
    }
}

# Use appropriate template per service
neighbor 192.168.1.1 { inherit unicast-peer; local-address 192.168.1.2; peer-as 65000; }
neighbor 192.168.2.1 { inherit flowspec-peer; local-address 192.168.2.2; peer-as 65000; }
neighbor 192.168.3.1 { inherit vpn-peer; local-address 192.168.3.2; peer-as 65000; }

Pattern 3: Automated Config Generation

Python script to generate config for 1000 peers:

#!/usr/bin/env python3

# Configuration data
config = {
    'router_id': '10.0.0.1',
    'local_as': 65001,
    'peers': []
}

# Generate 1000 peers
for i in range(1, 1001):
    config['peers'].append({
        'ip': f'192.168.{i//256}.{i%256}',
        'local_address': '10.0.0.1',
        'peer_as': 65000 + (i % 10),
        'description': f'Peer-{i}'
    })

# Write configuration
with open('/etc/exabgp/exabgp.conf', 'w') as f:
    # Template
    f.write("""
template {
    neighbor base-peer {
        router-id %s;
        local-as %s;

        family {
            ipv4 unicast;
        }

        capability {
            route-refresh;
        }
    }
}
""" % (config['router_id'], config['local_as']))

    # Neighbors
    for peer in config['peers']:
        f.write("""
neighbor %s {
    inherit base-peer;
    local-address %s;
    peer-as %s;
    description "%s";
}
""" % (peer['ip'], peer['local_address'], peer['peer_as'], peer['description']))

print(f"Generated config with {len(config['peers'])} neighbors")

Advanced Template Patterns

Pattern 1: API Process Templates

# Define processes
process healthcheck {
    run /etc/exabgp/api/healthcheck.py;
    encoder text;
}

process receive-routes {
    run /etc/exabgp/api/receive.py;
    encoder json;
    receive { parsed; updates; }
}

# Template with API
template {
    neighbor api-enabled {
        router-id 10.0.0.1;
        local-as 65001;

        family {
            ipv4 unicast;
        }

        api {
            processes [ healthcheck, receive-routes ];
        }
    }
}

# Use template
neighbor 192.168.1.1 {
    inherit api-enabled;
    local-address 192.168.1.2;
    peer-as 65000;
}

Pattern 2: Security Templates

# High-security template
template {
    neighbor secure-peer {
        router-id 10.0.0.1;
        local-as 65001;

        md5-password "ChangeThisPassword!";
        incoming-ttl 255;
        hold-time 90;

        family {
            ipv4 unicast;
        }

        capability {
            route-refresh;
            graceful-restart;
        }
    }
}

# Use for sensitive peers
neighbor 203.0.113.1 {
    inherit secure-peer;
    local-address 192.0.2.1;
    peer-as 13335;  # Cloudflare
    description "Critical Transit Provider";
    md5-password "ActualSecurePassword123!";  # Override default
}

Pattern 3: Multi-Hop iBGP Template

template {
    neighbor ibgp-rr-client {
        router-id 10.0.0.1;
        local-address 10.0.0.1;  # Loopback
        local-as 65001;
        peer-as 65001;  # iBGP

        outgoing-ttl 5;

        family {
            ipv4 unicast;
            ipv6 unicast;
            ipv4 mpls-vpn;
        }

        capability {
            route-refresh;
            graceful-restart 120;
        }
    }
}

# Route reflector clients
neighbor 10.0.0.2 { inherit ibgp-rr-client; description "RR Client 1"; }
neighbor 10.0.0.3 { inherit ibgp-rr-client; description "RR Client 2"; }
neighbor 10.0.0.4 { inherit ibgp-rr-client; description "RR Client 3"; }

Real-World Examples

Example 1: ISP Edge Router (10 Upstreams)

process announce-anycast {
    run /etc/exabgp/api/announce_anycast.py;
    encoder text;
}

template {
    neighbor upstream-provider {
        router-id 192.0.2.1;
        local-as 65001;

        family {
            ipv4 unicast;
            ipv6 unicast;
        }

        capability {
            route-refresh;
            graceful-restart;
        }

        api {
            processes [ announce-anycast ];
        }
    }
}

# 10 upstream providers
neighbor 203.0.113.1 { inherit upstream-provider; local-address 203.0.113.2; peer-as 174; description "Cogent"; }
neighbor 203.0.113.5 { inherit upstream-provider; local-address 203.0.113.6; peer-as 1299; description "Telia"; }
neighbor 203.0.113.9 { inherit upstream-provider; local-address 203.0.113.10; peer-as 3356; description "Level3"; }
# ... 7 more providers

Example 2: Data Center Fabric (100 Leaf Switches)

Config generation for 100 leafs:

#!/usr/bin/env python3

# Template
template = """
template {{
    neighbor leaf-switch {{
        router-id 10.0.0.1;
        local-address 10.0.0.1;
        local-as 65000;

        family {{
            l2vpn evpn;
        }}

        capability {{
            extended-message;
        }}
    }}
}}
"""

# Generate 100 leaf neighbors
with open('/etc/exabgp/exabgp.conf', 'w') as f:
    f.write(template)

    for rack in range(1, 11):  # 10 racks
        for leaf in range(1, 11):  # 10 leafs per rack
            leaf_id = (rack - 1) * 10 + leaf
            leaf_ip = f"10.{rack}.{leaf}.1"

            f.write(f"""
neighbor {leaf_ip} {{
    inherit leaf-switch;
    peer-as 65{leaf_id:03d};
    description "Rack{rack:02d}-Leaf{leaf:02d}";
}}
""")

Example 3: DDoS Mitigation Network (50 Scrubbing Centers)

process ddos-blocker {
    run /etc/exabgp/api/ddos_blocker.py;
    encoder text;
}

template {
    neighbor scrubbing-center {
        router-id 10.0.0.1;
        local-as 65001;

        family {
            ipv4 flow;
        }

        api {
            processes [ ddos-blocker ];
        }
    }
}

# Regional scrubbing centers (US)
neighbor 192.168.1.1 { inherit scrubbing-center; local-address 192.168.1.2; peer-as 65000; description "US-East-1 Scrubber"; }
neighbor 192.168.2.1 { inherit scrubbing-center; local-address 192.168.2.2; peer-as 65000; description "US-West-1 Scrubber"; }
# ... more US

# Regional scrubbing centers (EU)
neighbor 192.168.10.1 { inherit scrubbing-center; local-address 192.168.10.2; peer-as 65000; description "EU-West-1 Scrubber"; }
neighbor 192.168.11.1 { inherit scrubbing-center; local-address 192.168.11.2; peer-as 65000; description "EU-Central-1 Scrubber"; }
# ... more EU

Best Practices

1. Name Templates Descriptively

Bad:

template { neighbor t1 { /* ... */ } }
template { neighbor t2 { /* ... */ } }

Good:

template { neighbor ebgp-unicast-peer { /* ... */ } }
template { neighbor ibgp-vpn-peer { /* ... */ } }
template { neighbor flowspec-ddos-peer { /* ... */ } }

2. Document Template Purpose

# Template: eBGP peers for internet transit
# Used by: All upstream providers
# Families: IPv4/IPv6 unicast
# Features: Graceful restart, route refresh
template {
    neighbor ebgp-transit {
        router-id 10.0.0.1;
        local-as 65001;

        family {
            ipv4 unicast;
            ipv6 unicast;
        }

        capability {
            route-refresh;
            graceful-restart;
        }
    }
}

3. Keep Templates Simple

Bad: Overly complex template

template {
    neighbor complex-template {
        # 50+ lines of configuration
        # ...
    }
}

Good: Break into focused templates

template {
    neighbor base-peer { /* Core settings */ }
    neighbor ebgp-peer { /* eBGP specifics */ }
    neighbor ibgp-peer { /* iBGP specifics */ }
}

4. Use Configuration Generation for Complex Hierarchies

ExaBGP doesn't support template inheritance directly. For complex scenarios, generate config:

# config_generator.py
class TemplateRegistry:
    def __init__(self):
        self.templates = {}

    def register(self, name, config):
        self.templates[name] = config

    def generate(self, template_name, overrides):
        base = self.templates[template_name].copy()
        base.update(overrides)
        return base

# Use it
registry = TemplateRegistry()
registry.register('base', {'router_id': '10.0.0.1', 'local_as': 65001})
registry.register('ebgp', {**registry.templates['base'], 'peer_as': 65000})

# Generate neighbor
neighbor = registry.generate('ebgp', {'local_address': '192.168.1.2'})

5. Version Control Your Templates

# Store templates in version control
/etc/exabgp/
├── exabgp.conf              # Main config (generated)
├── templates/
│   ├── base.conf            # Base template
│   ├── ebgp.conf            # eBGP template
│   ├── ibgp.conf            # iBGP template
│   └── flowspec.conf        # FlowSpec template
├── peers/
│   ├── transit.yaml         # Transit peer data
│   └── customers.yaml       # Customer peer data
└── generate_config.py       # Config generator

Common Patterns

Pattern: eBGP + iBGP Separation

# eBGP template
template {
    neighbor ebgp-peer {
        router-id 10.0.0.1;
        local-as 65001;

        family { ipv4 unicast; ipv6 unicast; }
        capability { route-refresh; graceful-restart; }
    }
}

# iBGP template
template {
    neighbor ibgp-peer {
        router-id 10.0.0.1;
        local-address 10.0.0.1;
        local-as 65001;
        peer-as 65001;

        outgoing-ttl 5;

        family { ipv4 unicast; ipv6 unicast; ipv4 mpls-vpn; }
        capability { route-refresh; graceful-restart 120; }
    }
}

# eBGP neighbors
neighbor 203.0.113.1 { inherit ebgp-peer; local-address 203.0.113.2; peer-as 174; }

# iBGP neighbors
neighbor 10.0.0.2 { inherit ibgp-peer; }
neighbor 10.0.0.3 { inherit ibgp-peer; }

Pattern: Staged Rollout

# Old template (existing neighbors)
template {
    neighbor legacy-peer {
        hold-time 180;
        capability { route-refresh; }
    }
}

# New template (improved settings)
template {
    neighbor modern-peer {
        hold-time 90;  # Faster failure detection
        capability { route-refresh; graceful-restart; }  # Added GR
    }
}

# Migrate neighbors gradually
neighbor 192.168.1.1 { inherit legacy-peer; /* ... */ }
neighbor 192.168.2.1 { inherit modern-peer; /* ... */ }  # Migrated
neighbor 192.168.3.1 { inherit legacy-peer; /* ... */ }

Troubleshooting

Problem: Template Not Found

Error:

Configuration error: template 'my-template' not found

Cause: Template not defined before use.

Fix:

# Define template BEFORE use
template {
    neighbor my-template { /* ... */ }
}

# Then use it
neighbor 192.168.1.1 {
    inherit my-template;
    # ...
}

Problem: Unexpected Override Behavior

Scenario:

template {
    neighbor my-template {
        hold-time 180;
    }
}

neighbor 192.168.1.1 {
    inherit my-template;
    hold-time 90;
}

Question: Why is hold-time 90, not 180?

Answer: Child configurations override template values. This is expected behavior.


Problem: Family Not Merged as Expected

Scenario:

template {
    neighbor my-template {
        family { ipv4 unicast; }
    }
}

neighbor 192.168.1.1 {
    inherit my-template;
    # Expecting both ipv4 and ipv6, but only have ipv4
}

Fix: Explicitly declare both families:

neighbor 192.168.1.1 {
    inherit my-template;
    family {
        ipv4 unicast;  # From template (merged)
        ipv6 unicast;  # Added
    }
}

Problem: group Is Not a Section

The group block of ExaBGP 3.4 no longer exists. Each neighbor inherits a template instead, and a configuration generator covers the cases where that becomes repetitive.

Instead of:

# Not a section: the parser rejects this
group my-group {
    inherit my-template;
    # ...
}

Use:

# Python config generator
for peer in group_members:
    print(f"""
neighbor {peer.ip} {{
    inherit my-template;
    local-address {peer.local_address};
    peer-as {peer.peer_as};
}}
""")

See Also


Ready to scale? Start with Quick Start Guide


Clone this wiki locally