,

Cisco Switch Configuration Best Practices: Build Template

Cisco switch build template — dark title card reading Cisco Switch Configuration Best Practices

Key Takeaways

  • One template for every switch, every site. You review one config and you’ve reviewed them all, which is the whole point.
  • Authentication in 2026 is type 9 scrypt, 4096-bit RSA, SSHv2, min-length 12. Type 5 (MD5), 1024-bit keys and type 7 passwords all died years ago, and a 2020 template will still ship all three.
  • TACACS+ with local fallback everywhere, including the console. Add aaa authorization console so the break-glass port can’t bypass command authorization.
  • Every port gets an explicit role: hard mode, switchport nonegotiate, storm-control, port-security, and portfast with bpduguard on access ports. Unused ports are shut in a parking-lot VLAN.
  • Never set a low spanning-tree priority on an access switch. Combined with root guard on the uplink, it blocks the uplink and isolates the switch while every access port stays lit. Priority belongs on the core, and nowhere else.
  • The build template at the end is complete. Fill every <PLACEHOLDER> before the switch ever touches the network.

Why I use one template for every switch

In 2020 I built a switch template for a production network of a few hundred ports. It had VTP client mode, type 5 enable secrets, a 1024-bit RSA key, CDP running everywhere, SNMP v2c, and the native VLAN set to 1. Every one of those lines is a security finding in 2026.

The rest of that template was good. That’s exactly why the whole thing is worth keeping rather than starting over. The port definitions, the AAA structure, the fail-safes against locking yourself out: that work doesn’t expire. The defaults Cisco ships and the hashing Cisco recommends do.

That is the lesson worth carrying out of a real deployment: the structure survived six years intact, and every line carrying a crypto default or a legacy protocol quietly became a finding without anyone touching the config. Nothing broke. Nothing alarmed. The template simply aged into non-compliance while sitting still. So this post is written as a diff against the old template rather than a from-scratch guide, because the diff is the part that actually teaches you something.

So this is the 2026 rebuild, sanitized. No site names, no server names, no secrets. Everything client-specific is a <PLACEHOLDER> you fill in, and the full config is at the bottom if you want to skip the commentary.

What changed since the 2020 template

Here is the honest diff. Old defaults, the current state, and why the change happened.

Area2020 template2026Why it changed
VTPserver / clientvtp mode offOne bad VLAN database sync flattens the network. VTPv3 is technically alive; nobody should run it.
enable secrettype 5 (MD5)type 9 (scrypt)MD5 hashes fall to a GPU rig in minutes to hours, depending on the password.
SSH key1024-bit RSA4096-bit RSA1024-bit RSA has been off NIST’s approved list for over a decade.
Passwordstype 7 where it mattered leasttype 9, security passwords min-length 12Type 7 is reversible with a five-line script. service password-encryption protects nothing you care about.
SSHv1 and v2 mixedv2 only, ip ssh dh min size 2048SSHv1 is broken and should have died in 2001.
CDPonno cdp runCDP hands hostnames, IPs and platform info to anyone with a cable.
SNMPv2c communitiesv3 priv, or v2c locked behind an ACLA community string is a password that goes cleartext on the wire.
Spanning treeclassic STP, no guard defaultsrapid-pvst with portfast, bpduguard and loopguard defaultsFast convergence, and a rogue cable can’t wedge the network.
Native VLANuntagged VLAN 1vlan dot1q tag native, VLAN 1 SVI shutUntagged native traffic is the classic VLAN-hopping surface.
Control planenot in the templateCoPP policyThe CPU is the DoS target now, and Cisco won’t mention it on the box.

None of this is exotic. Every item above is a documented default, a documented deprecation, or a documented attack — Cisco’s own IOS XE hardening guide and the older Harden IOS Devices document cover most of them, and NIST covers the crypto. The 2020 template just predates the part where they all landed at once.

Global hardening: the lines that stop the easy attacks

These lines are the same on every switch I build. None of them cost you anything in traffic or management, and each one removes a class of problem.

Crypto: type 9, 4096 bits, SSHv2

The two commands that matter most, and the two that most old templates get wrong.

enable algorithm-type scrypt secret <ENABLE_SECRET>
username <ADMIN_USER> privilege 15 algorithm-type scrypt secret <ADMIN_SECRET>

security passwords min-length 12

! generate on the exec line after hostname and domain are set
crypto key generate rsa modulus 4096

ip ssh version 2
ip ssh time-out 60
ip ssh authentication-retries 3
ip ssh dh min size 2048

ip ssh server algorithm encryption aes256-ctr aes192-ctr aes128-ctr
ip ssh server algorithm mac hmac-sha2-256 hmac-sha2-512

Type 9 is scrypt. It has been in IOS and IOS-XE for well over a decade, so there is no excuse for a new config that says enable secret 5. Type 8 (PBKDF2) is a middle ground if you ever hit a platform that balks; type 9 is the answer.

crypto key generate rsa without a modulus still defaults low on plenty of platforms, so old configs are full of 1024-bit keys. 4096 is the number; if an older box caps at 2048, take the cap and document it — 2048 is still the floor NIST SP 800-131A Rev. 2 allows, and 1024 has been disallowed for years. ip ssh dh min size 2048 stops the SSH key exchange from downgrading to small Diffie-Hellman groups.

The two ip ssh server algorithm lines are the part most templates still miss. A default IOS-XE image will happily negotiate 3DES and hmac-sha1 with an old client, which means your 4096-bit key protects a session wrapped in ciphers your auditor will flag. Pinning the list stops the downgrade. The exact keywords vary by release, so type ip ssh server algorithm encryption ? on the box and take the strongest set it offers — newer images add the AES-GCM suites, and Cisco documents the full matrix in the SSH algorithms guide. Set these after you have a working session, and keep that session open until you have proved a new one connects.

One absurdity worth naming: service password-encryption still ships in every vendor hardening guide, and it protects exactly one thing, the type 7 cipher that a script reverses in milliseconds. If that line is your only password defence, it’s a feature request, not a fix. The fix is type 9 and a minimum length.

Logging that doesn’t leak secrets

Syslog is where configs go to confess. Every show running-config you paste or archive is a copy of every key on the box, unless you do this:

archive
 path flash:config-archive
 maximum 20
 log config
  hidekeys
  logging enable

logging buffered 16384 debug
logging trap notifications
no logging console
logging source-interface Vlan<MGMT_VLAN>

service timestamps debug datetime msec localtime show-timezone
service timestamps log datetime msec localtime show-timezone

hidekeys replaces secret material with <removed> in the archive log and in anything that replays it, which includes the config that syslog might carry. Verify with show archive log config all; you want to see commands, not keys.

no logging console stops debug output from melting the console session, which matters because the console is your break-glass. If you want errors on the console anyway, logging console limited is the gentler version. Source-interface and full timestamps are so that a log correlation tool can actually sort your events.

NTP: authenticated or not at all

Wrong time breaks more than log correlation. Certificate validation for 802.1X fails, TACACS+ packets get dropped by your own filter, and an attacker who can skew a switch clock can make the logs lie about when things happened.

ntp authenticate
ntp authentication-key 1 hmac-sha2-256 <NTP_KEY>
ntp trusted-key 1
ntp server <NTP_SERVER_IP> key 1
ntp source Vlan<MGMT_VLAN>

That is the whole trick: ntp authenticate plus a trusted key. Every NTP implementation in production speaks this. There is no excuse for unauthenticated NTP in 2026, and yet most switch configs I see still omit the three auth lines.

A note on the algorithm, because it looks like a contradiction with the enable secret section. Current IOS-XE takes md5, cmac-aes-128, hmac-sha1 or hmac-sha2-256 in that slot (see Cisco’s NTP configuration guide), and the old templates all say md5. NTP’s md5 is a keyed digest over the packet, not a password hash, so it is not broken the same way a type 5 secret is — but there is no reason to keep it when the box will do SHA-256, and a FIPS-constrained environment will reject it outright. Use hmac-sha2-256, keep the key at or under 32 characters, and fall back to md5 only on an image that has nothing better. Both ends have to agree, so change the server first.

The boring services

These lines look like superstition until the day one of them is the difference between an incident and a footnote.

no service pad
no service tcp-small-servers
no service udp-small-servers
service tcp-keepalives-in
service tcp-keepalives-out

no ip source-route
no ip domain-lookup
no ip http server
no ip http secure-server

ip domain name <DOMAIN>

no service pad kills the X.25 packet assembler/disassembler service, a relic of a network type almost nobody has run this century. The small-servers lines kill echo, discard, daytime and chargen on TCP 7, 9, 13 and 19, plus echo, discard, chargen and time (RFC 868, UDP 37) on the UDP side — all of them reflector and amplification fodder before most of us had jobs. Both small-server sets have been off by default since IOS 12.0, so these two lines are belt-and-braces rather than a fix. They cost nothing and they make the intent explicit to the next person who reads the config, which is the entire argument for putting them in a template. TCP keepalives both ways mean a dead management session stops holding a VTY line forever.

no ip http server and no ip http secure-server turn off the web UIs nobody uses on a switch. If you genuinely use RESTCONF or a guest portal, leave the secure one on and say so in the change record. no ip domain-lookup stops the classic thirty-second pause when someone typos a command and the switch tries to resolve it as a hostname.

AAA that can’t lock you out

AAA is where templates go to die, because the failure mode is catastrophic. The TACACS server goes down, and suddenly every engineer on the network can’t log in. The fix is structural, not a prayer: TACACS+ for everything, local as the automatic fallback, and the console as the final break-glass that never depends on the network.

The structure

aaa new-model

tacacs server TACACS1
 address ipv4 <TACACS_SERVER_IP>
 key <TACACS_KEY>

aaa group server tacacs+ TACACS
 server name TACACS1

aaa authentication login default group TACACS local
aaa authentication login CONSOLE local
aaa authorization exec default group TACACS local
aaa authorization commands 15 default group TACACS local
aaa authorization console
aaa accounting exec default start-stop group TACACS
aaa accounting commands 15 default group TACACS
aaa accounting network default start-stop group TACACS

Read the auth lines as group TACACS local: try TACACS+, and if it’s unreachable, fall back to local. That ordering is what makes an outage survivable. The local database must contain a privilege 15 user, because fallback to an empty local database is just a slower lockout.

Why TACACS+ and not RADIUS for device admin? TACACS+ encrypts the whole packet, not just the password, and it separates authentication, authorization and accounting into distinct decisions. That separation is what lets you authorize commands, not just logins. RADIUS is for network access (802.1X, VPN); TACACS+ is for device admin. Both have their jobs, and mixing them up is how you get a network where “the server is up” and “nobody can log in” are both true.

The console is the break-glass

If the console depends on TACACS+, your break-glass is made of the same glass as the door. The console gets its own local-only list, so it works with the server down and with the network down:

line con 0
 exec-timeout 10 0
 logging synchronous
 login authentication CONSOLE

line vty 0 15
 access-class MGMT-ACL in
 exec-timeout 10 0
 login authentication default
 transport input ssh

Now the failure story is the one you want: TACACS dies, VTY logins fall back to local, and the console works regardless, with a local privilege 15 user as the last card in the deck. Serial console access still means being physically at the switch or on an out-of-band terminal server, which is exactly what a break-glass is for.

aaa authorization console: the line everyone skips

Here is the absurdity that costs real networks their audit. Every hardening guide tells you to authorize commands 15, so a user can’t run configure terminal unless TACACS+ says so. Almost none tell you that authorization does not apply to the console line by default. Console access bypasses command authorization entirely, by design.

So the “secured” switch has a back door: anyone with console access runs any command, and the audit trail shows nothing. aaa authorization console closes it. Cisco documents the behaviour in the Harden IOS Devices guide; almost nobody reads that far. Without the line, your command authorization is a decorative feature.

I know this because the first version of this template got it wrong. It authorized commands 15 on the VTY lines, the config passed its own review, and it was a later line-by-line walk-through that caught the console running anything it liked with no accounting record behind it. Nothing had gone wrong, which is exactly the problem: the gap produces no symptom until someone uses it. One line closed it, and it has been in every template I have written since.

One rule before you switch it on: the console list must have local fallback and a local privilege 15 user, or you authorize the console right into a locked box. With this config, a console user authenticates locally and the same command authorization applies, enforced against TACACS+ with local fallback. Test the lockout path before you deploy, not after.

Accounting: the audit trail you’ll want in June

The three accounting lines above record every exec session, every command 15, and every network service interaction, start to stop, to TACACS+. When a config appears on forty switches and nobody admits to it, this is how you find out who, when, and from where. TACACS+ accounting also captures the commands that show logging never will, because hidekeys is scrubbing the local archive.

The management plane: who gets in and how

Management traffic is the one class of traffic that every switch must carry and no user traffic should touch. The template separates it hard: a dedicated VLAN, an ACL that names who may even try to log in, and SNMP that doesn’t shout credentials on the wire.

VTY ACL: who may try to log in at all

ip access-list standard MGMT-ACL
 permit <MGMT_SUBNET> <WILDCARD>
 deny any log

line vty 0 15
 access-class MGMT-ACL in

The VTY ACL is your first filter, applied before authentication and before TACACS+. Everything outside the management subnet gets a connection refused instead of an SSH login prompt, which also means no password guessing from the user networks. It is a standard ACL on a network that has long outgrown the name.

Three details that bite. The wildcard mask is not optional: permit 10.10.40.0 0.0.0.255 is a /24, while permit 10.10.40.0 on its own is a single-host entry and will lock out every address but one. Get that wrong at 2am and you have built the outage yourself. The ACL binds to the VTY line, so it filters inbound SSH and nothing else, which is what you want. And the implicit deny any is already there, so the permit is the whole policy — the explicit deny any log is only there to give you hit counters when someone asks who has been knocking.

Management SVI and VLAN

vlan <MGMT_VLAN>
 name MGMT

interface Vlan<MGMT_VLAN>
 description MGMT SVI
 ip address <MGMT_IP> <MGMT_MASK>
 no shutdown

ip default-gateway <MGMT_GATEWAY>

Management lives on its own VLAN, with its own SVI, and nothing user-facing routes to it. On a pure L2 switch the ip default-gateway line is what makes the SVI reachable off-box; on an L3 switch you’d route it like any other VLAN. The 2020 template did this too, and it’s still right.

One rule from experience: the management VLAN must exist on the trunk allowed lists of every uplink, or a freshly built switch disappears the moment you plug it in. The template’s trunk lines include it for exactly that reason.

SNMP: v3, or v2c with handcuffs

SNMP v2c community strings are passwords that travel cleartext, and your NMS vendor will tell you v2c is fine until the day someone sniffs one. The right answer is v3 with auth and privacy:

snmp-server group <SNMP_GROUP> v3 priv
snmp-server user <SNMP_USER> <SNMP_GROUP> v3 auth sha <SNMP_AUTH_KEY> priv aes 128 <SNMP_PRIV_KEY>
snmp-server host <NMS_IP> version 3 priv <SNMP_USER>
snmp-server location <LOCATION>
snmp-server contact <CONTACT>

priv means the user needs a privacy key, so polling data is encrypted, not just authenticated. AES-128 is the safe baseline; newer IOS-XE accepts AES-256 in the same spot if your NMS does too. The user line above creates the user locally; on a box with RADIUS you can also do remote users, but local keeps the template self-contained.

If your NMS genuinely cannot speak v3 (it’s 2026, but the budget sometimes isn’t), the handcuffed version is a long community string, read-only, restricted to the NMS host with an ACL: snmp-server community <SNMP_COMMUNITY> ro MGMT-ACL. Note that still puts the string on the wire in cleartext for any packet capture, so treat it as a stopgap with a dated ticket to migrate.

CDP: off, and why

Cisco Discovery Protocol runs by default on every Catalyst, and it is a gift to anyone who plugs into your network. A ten-second show cdp neighbors on an unknown switch in your wiring closet tells them your hostnames, platform types, IPs and software versions. That is a reconnaissance report, free of charge.

no cdp run

One line, globally. If a specific device genuinely needs CDP, a WAP that uses it for PoE negotiation, for example, re-enable it per interface with cdp enable on that port only. LLDP is the open standard and the better default if your fleet speaks it; just know it leaks the same categories of information to the same audience.

VLANs and spanning tree: manual, tagged, and guarded

This section is where the 2020 template and the 2026 version disagree most. VLAN management goes manual, VLAN 1 gets retired, and spanning tree stops trusting the network.

VTP: turn it off, then create VLANs by hand

VTP is the protocol where one switch announces its VLAN database to the whole domain, and the whole domain obeys. Cisco still ships Catalyst switches in VTP server mode. The default state of a 2026 switch is therefore one bad database sync away from flattening your VLANs across a building, and the 2020 template ran VTP client mode, which is the same risk with fewer opinions.

vtp mode off

off means the switch neither participates nor relays VTP advertisements. If your platform only offers transparent, take it; transparent relays messages but applies nothing, which is the next best thing. Creating a VLAN on every switch by hand is ten seconds of typing and removes an entire class of network-wide accidents. That is the trade the vendor default asks you not to make.

VLAN 1: shut it down, tag the native

interface Vlan1
 shutdown

vlan dot1q tag native

VLAN 1 is the default native VLAN, the default management candidate, and the trunk that every untagged frame lands on. You cannot delete it, so the template does the next best thing: the SVI is shut, and vlan dot1q tag native makes the switch tag native-VLAN traffic on trunks like any other VLAN.

Tagging the native VLAN closes the classic VLAN-hopping surface where an attacker double-tags frames and rides them across your trunk onto a foreign VLAN. Two caveats. Every device on the trunk must accept tagged native traffic, which means lab-testing before a wide rollout, and both ends of the trunk should match, because a mismatch silently breaks the native VLAN’s traffic. The mechanism behind this is worth understanding in full — see the deep-dive on the native VLAN, where the double-tagging attack and the mismatch failure mode are walked through with packet captures. The 2020 template left native traffic untagged on VLAN 1, which was the standard of its time and is the finding of ours.

Root bridge: explicit, and only on the switch that should win

spanning-tree mode rapid-pvst

! Core and distribution only. Leave an access switch at the default 32768.
spanning-tree vlan <VLAN_LIST> priority <STP_PRIORITY>

Rapid PVST+ is the default mode on modern code anyway, so that line is documentation as much as config. The priority is the part that matters, and it is the one value in this whole template that is not safe to copy: 4096 on the core, 8192 on distribution, and nothing at all on an access switch. An explicit low priority on the core means a rogue switch plugged into a wall port cannot win root by advertising a lower bridge ID, and root guard on the uplinks covers the rest.

Put a low priority on an access switch and you have built one of two outages, both of them yours. If the core still wins the election, its superior BPDU arrives on your uplink — the port that this template gives spanning-tree guard root — and root guard does exactly what you asked it to: it puts the port into root-inconsistent state and blocks it. Every access port on the switch stays up, link lights and all, and the switch has no path to the network.

Root guard blocking the uplink of an access switch that was given a low spanning-tree priority A core switch with priority 4096 sends a superior BPDU down the uplink to an access switch that was also configured with priority 4096. Because the uplink port carries spanning-tree guard root, the superior BPDU puts the port into root-inconsistent state and the port blocks, isolating the access switch from the network while all of its access ports remain up. CORE spanning-tree priority 4096 superior BPDU Gi1/0/24 — spanning-tree guard root root-inconsistent — port BLOCKING ACCESS priority 4096 — copied from the template access ports up · zero path to the network
Root guard and a low access-switch priority are individually correct and jointly an outage. The uplink blocks, the access ports stay up, and nothing in show interface status looks wrong.

The other outcome is worse. If your access switch wins the tie — same priority, lower MAC address — it becomes the root bridge for the whole VLAN, and every path in the building is now recalculated around a switch in a wiring closet. Traffic that should cross the core in one hop takes the scenic route through an access-layer uplink sized for a wiring closet, and you spend a morning wondering why the network got slow. This is why the priority line carries a placeholder and a comment instead of a number.

Portfast, bpduguard, loopguard: defaults for access

spanning-tree portfast default
spanning-tree portfast bpduguard default
spanning-tree loopguard default

Three global defaults, and they do most of the loop protection work for you. Portfast puts access ports into forwarding immediately instead of crawling through STP timers, which is what makes a laptop come up in seconds. Bpduguard turns any port that receives a BPDU into an err-disabled port, which is how you catch the switch somebody plugged into a wall port. Loopguard catches the subtler failure, a unidirectional link where a blocking port never hears its BPDU and silently starts forwarding.

Per-port spanning-tree bpduguard enable and spanning-tree portfast in the interface templates below make the intent visible at the interface, even though the defaults already cover access ports. One conflict to remember: root guard and loopguard cannot both apply to the same port, so a port with explicit spanning-tree guard root (the uplinks) is exempt from the loopguard default.

UDLD on the uplinks, aggressive

Fiber transceivers can fail in one direction, and a unidirectional link is the failure spanning tree and loop guard both go quiet for while traffic blackholes. UDLD aggressive actively probes the far end and err-disables the port within seconds when a link goes one-way.

There are two forms of the command and they are not interchangeable. The global one arms every fiber port on the box and nothing else:

udld aggressive

Copper ports are not covered by that line. They need the per-interface form, which is also the spelling you want in an interface template because it makes the intent visible at the port:

interface GigabitEthernet1/0/24
 udld port aggressive

Cisco is explicit about the split in the UDLD configuration guide: global for fiber, udld port at the interface for copper. The old advice that “UDLD only works on fiber” comes from people who only ever typed the global command. A copper uplink between two stacks in adjacent racks can go unidirectional exactly like a fiber one, and it will not be covered unless you say so per port.

DHCP snooping, DAI, IP source guard: the user-VLAN trio

ip dhcp snooping
ip dhcp snooping vlan 10,20
no ip dhcp snooping information option

ip arp inspection vlan 10,20
ip arp inspection validate src-mac dst-mac ip

Three features, one story. DHCP snooping builds a binding table of who got which IP from which trusted server, and silently drops offers from untrusted ports, which kills rogue DHCP servers stone dead. DAI (Dynamic ARP Inspection) checks ARP replies against that same table, which kills ARP spoofing. IP source guard, per interface, drops traffic from an IP the binding table doesn’t know, which kills IP spoofing on user ports.

The no ip dhcp snooping information option line is a 2020-to-2026 fix in its own right. Option 82 works fine when your DHCP server and relays are all Cisco. With almost anything else, the inserted option becomes the ticket you chase for a week: “no leases on VLAN 20” turns out to be the server rejecting option 82 payloads. If your fleet is homogeneous Cisco, keep the option; otherwise turn it off.

I learned that one the expensive way. A “no DHCP on this VLAN” ticket had already survived two rounds of port-level debugging — cables, VLAN membership, trunk allowed lists, the usual list — before anyone looked at what the switch was adding to the relayed packets rather than at what it was forwarding. The server was rejecting the option 82 payload and dropping the request silently, so every test at the port looked clean. The single line that disabled the insertion fixed it, and it has shipped as a default in the template ever since, which is the only reason it is in this post.

Two gotchas that cost real outages. The uplink ports must be marked ip dhcp snooping trust and ip arp inspection trust, or the switch eats the traffic it should relay, including the DHCP replies from your real server. And hosts with static IPs need entries in the binding table or DAI and IP source guard will starve them: ip source binding <MAC> vlan <VLAN> <IP> interface <INT> per host, or an ARP ACL via ip arp inspection filter.

Interface templates: the part you’ll actually copy

This is the gold of the whole post. Every port on the switch gets one of six roles, and every role has a fixed config. The engineer in the field never decides anything about a port, because the decision was made in this section, once, years ago.

Every template shares the same bones: explicit mode, switchport nonegotiate so DTP has nothing to negotiate (the full DTP story is here), storm-control so a broadcast loop doesn’t melt the uplink, port-security where a single host belongs, and portfast plus bpduguard on anything that touches an end device.

Access port with voice VLAN

interface GigabitEthernet1/0/1
 description <SITE> <PORT_LABEL> VOICE+DATA
 switchport mode access
 switchport access vlan <USER_VLAN>
 switchport voice vlan <VOICE_VLAN>
 switchport nonegotiate
 switchport port-security maximum 2
 switchport port-security violation restrict
 switchport port-security mac-address sticky
 storm-control broadcast level 5
 storm-control multicast level 10
 spanning-tree portfast
 spanning-tree bpduguard enable
 ip verify source port-security
 no shutdown

The voice VLAN makes the phone tag its own traffic and the PC ride untagged, which is the standard IP phone arrangement. Port-security maximum 2 is phone plus PC, and sticky learns the two MACs so a reboot doesn’t re-negotiate. Violation action is restrict on purpose: the port keeps working and the event log gets an entry, rather than a user port dying because somebody moved a cable.

Watch the maximum on real handsets, though. Two is the theoretical count and plenty of deployments run it happily, but some phone models present an extra MAC on the voice VLAN, and a docking station or a small desk switch behind the phone blows the limit instantly. If show port-security interface starts reporting violations on ports where nothing has changed, raise it to 3 rather than assuming somebody plugged in a hub. The per-VLAN form, switchport port-security maximum 1 vlan access alongside maximum 2 vlan voice, is the precise version if your platform supports it.

Access port without voice

interface GigabitEthernet1/0/2
 description <SITE> <PORT_LABEL> DATA
 switchport mode access
 switchport access vlan <USER_VLAN>
 switchport nonegotiate
 switchport port-security maximum 1
 switchport port-security violation restrict
 switchport port-security mac-address sticky
 storm-control broadcast level 5
 storm-control multicast level 10
 spanning-tree portfast
 spanning-tree bpduguard enable
 ip verify source port-security
 no shutdown

Same bones, one host. Maximum 1 is the honest limit for a desk with no phone. Everything else is identical, which is the point of templates: the diff between a voice port and a data port is two lines.

Trunk uplink

interface GigabitEthernet1/0/24
 description UPLINK <CORE_SWITCH> <CORE_PORT>
 switchport mode trunk
 switchport trunk native vlan <NATIVE_VLAN>
 switchport trunk allowed vlan <ALLOWED_VLAN_LIST>
 switchport nonegotiate
 spanning-tree guard root
 udld port aggressive
 ip dhcp snooping trust
 ip arp inspection trust
 no shutdown

The explicit allowed list is the whole point of a trunk. switchport trunk allowed vlan 10,20,40 means the trunk carries exactly what it should, and a VLAN added to the switch later is not automatically on the uplink. That is a feature, not a chore.

Root guard pins this port as the path to the real root bridge, UDLD covers the fiber failure mode, and the two trust lines keep DHCP snooping and DAI from eating the relayed traffic. Note there is no switchport trunk encapsulation dot1q line; that only exists on the ISL-era platforms, and demanding it on modern code is a syntax error waiting to happen.

WAP port

interface GigabitEthernet1/0/3
 description WAP <SITE> <WAP_NAME>
 switchport mode trunk
 switchport trunk native vlan <WAP_MGMT_VLAN>
 switchport trunk allowed vlan <WAP_SSID_VLANS>
 switchport nonegotiate
 spanning-tree portfast trunk
 spanning-tree bpduguard enable
 no shutdown

A wireless access point is a trunk port in a box. The management interface of the WAP rides untagged on the native VLAN, each SSID rides tagged, and the allowed list names the SSID VLANs explicitly. spanning-tree portfast trunk is the correct spelling for this port; plain portfast on a trunk is a warning waiting for you to misread it.

Leave bpdufilter off. Bpduguard shuts the port and logs the event when a BPDU shows up. Bpdufilter silently eats BPDUs, and if that WAP ever bridges, say a wired mesh backhaul or a client behind the AP, you’ve just hidden a loop from spanning tree. Guard catches, filter hides.

Router uplink

interface GigabitEthernet1/0/4
 description ROUTER <SITE> <ROUTER_NAME> <PORT>
 switchport mode access
 switchport access vlan <MGMT_VLAN>
 switchport nonegotiate
 spanning-tree portfast
 spanning-tree bpduguard enable
 no shutdown

The router that sits next to the switch, usually the out-of-band or site gateway, lands on the management VLAN as an access port. A router doesn’t create STP loops, so portfast applies without drama, and bpduguard stays on in case the cable lands in the wrong port.

Unused port

interface GigabitEthernet1/0/23
 description UNUSED
 switchport mode access
 switchport access vlan <PARKING_VLAN>
 switchport nonegotiate
 switchport port-security maximum 1
 switchport port-security violation shutdown
 spanning-tree portfast
 spanning-tree bpduguard enable
 shutdown

Every unused port is shut, sitting in a parking-lot VLAN that has no SVI, no DHCP, and no route anywhere. The violation action flips to shutdown here, deliberately: the only MAC that belongs on this port is the one that will never appear, so any frame is a red flag and the port should die. This is the port template that makes finding unused ports a solved problem instead of an audit task.

802.1X and ISE, when you have them

If you have a RADIUS backend with NAC policy, IEEE 802.1X with MAB is the upgrade path for every access port in this post. The switch-side config is small, because the policy lives on the server. If you don’t have it yet, the port-security lines in the templates above are your placeholder, and they stay in place until the server exists.

radius server ISE-1
 address ipv4 <ISE_IP> auth-port 1812 acct-port 1813
 key <RADIUS_KEY>

aaa group server radius RADIUS
 server name ISE-1

aaa authentication dot1x default group RADIUS
aaa authorization network default group RADIUS
dot1x system-auth-control

That is the global half: a RADIUS server, a group, and the two AAA lines that make 802.1X authentication and network authorization consult it. dot1x system-auth-control is the master switch that lets per-port dot1x actually enforce. The per-port half replaces the port-security block on the voice and data templates:

interface GigabitEthernet1/0/1
 authentication port-control auto
 authentication host-mode multi-domain
 authentication priority dot1x mab
 authentication timer reauthenticate 3600
 mab
 dot1x pae authenticator
 dot1x timeout tx-period 15
 spanning-tree portfast
 spanning-tree bpduguard enable

port-control auto starts the port unauthorized and lets the server decide. MAB (MAC Authentication Bypass) is what gets devices that can’t speak 802.1X, phones in particular, onto the network by MAC address instead. multi-domain host mode is the voice-plus-data arrangement, one domain per device type. On older code the keyword is authentication order dot1x mab instead of priority; same meaning, newer spelling.

The catch, as always, is the server. ISE wants certificates, identity groups, policy sets, and its own learning curve, which is a post of its own, and I’ve written it: the full first-deployment walkthrough is Cisco ISE: First Deployment. Until that exists, port-security is what your ports get, and it is honestly not a bad seatbelt for a network that doesn’t have a NAC yet.

Control-plane policing: the 2026 addition

Every switch has a CPU, and every CPU can be flooded. The management protocols this template enables, SSH, SNMP, NTP, TACACS+, all land on the control plane, and so does every broadcast storm and every scanner on your user VLANs. CoPP lets you rate-limit what the CPU has to swallow, and it is the one feature in this post that Cisco ships without telling you about.

ip access-list extended MGMT-TRAFFIC
 permit tcp any any eq 22
 permit tcp any any eq 49
 permit udp any any eq 161
 permit udp any any eq 123

class-map match-all MGMT-TRAFFIC
 match access-group name MGMT-TRAFFIC

policy-map COPP
 class MGMT-TRAFFIC
  police cir 1000000 conform-action transmit exceed-action drop
 class class-default
  police cir 256000 conform-action transmit exceed-action drop

control-plane
 service-policy input COPP

The policy gives the management protocols a healthy rate and polices everything else hard. The ACL matches the same protocols the config opens up — SSH, TACACS+, SNMP and NTP — so legitimate management keeps flowing while a scan or a storm gets dropped at the CPU input.

First, check that your platform speaks this dialect at all, because CoPP is not one feature with one syntax. The control-plane plus service-policy input form above is the IOS-XE model: Catalyst 3850, Catalyst 9000 and their relatives, documented in Cisco’s CoPP configuration guide. Older Catalyst hardware does control-plane protection, but through a completely different command set — on a 3560-X or 3750-X you are writing mls qos copp protocol <protocol> police <rate> against a fixed list of named protocols, not attaching a policy-map. And the small-form switches are not the have-nots you might assume: the 2960-CX and 3560-CX guides have a CoPP chapter of their own. Read your platform’s guide before you assume either that you have this or that you don’t.

Second, lab it. class-default policing touches everything else the CPU processes, including routing protocol hellos if your switch runs any, and policing those into the ground is how you lose a neighbour adjacency at 2am with a config change nobody connects to the symptom. Test on a spare switch, watch show policy-map control-plane counters for conform and drop rates under normal load first, then decide your numbers. Roll it out to one closet before you roll it out to a building.

That last point is why CoPP is in this post and not in the copy-paste template below. The template is the safe floor, verified by deployment. CoPP is the tuned layer you add after testing, per platform, with counters watched.

The build template

The complete, sanitized config. Fill every <PLACEHOLDER>, check the notes after the block, and it will survive contact with a production network. VLAN numbers below are examples; the roles are what matter.

! ============================================================
! PRODUCTION SWITCH BUILD TEMPLATE - IOS / IOS-XE
! Fill every <PLACEHOLDER> before deployment.
! <SITE> = site short code, e.g. bldg-a
! ============================================================
hostname <HOSTNAME>

! --- Global services: kill what you don't use ----------------
no service pad
no service tcp-small-servers
no service udp-small-servers
service tcp-keepalives-in
service tcp-keepalives-out

no ip source-route
no ip domain-lookup
ip domain name <DOMAIN>

no ip http server
no ip http secure-server

security passwords min-length 12

! --- Authentication: type 9, nothing weaker -----------------
enable algorithm-type scrypt secret <ENABLE_SECRET>
username <ADMIN_USER> privilege 15 algorithm-type scrypt secret <ADMIN_SECRET>

! --- SSH: generate the key on the exec line first ------------
! crypto key generate rsa modulus 4096
ip ssh version 2
ip ssh time-out 60
ip ssh authentication-retries 3
ip ssh dh min size 2048
! confirm the keyword list your image supports with `?` first
ip ssh server algorithm encryption aes256-ctr aes192-ctr aes128-ctr
ip ssh server algorithm mac hmac-sha2-256 hmac-sha2-512

! --- Login logging: failed attempts are the early warning ----
login on-failure log
login on-success log
login block-for 120 attempts 5 within 60

! --- AAA: TACACS+ first, local as the safety net -------------
aaa new-model

tacacs server TACACS1
 address ipv4 <TACACS_SERVER_IP>
 key <TACACS_KEY>

aaa group server tacacs+ TACACS
 server name TACACS1

aaa authentication login default group TACACS local
aaa authentication login CONSOLE local
aaa authorization exec default group TACACS local
aaa authorization commands 15 default group TACACS local
aaa authorization console
aaa accounting exec default start-stop group TACACS
aaa accounting commands 15 default group TACACS
aaa accounting network default start-stop group TACACS

! --- Config archive with hidekeys ----------------------------
archive
 path flash:config-archive
 maximum 20
 log config
  hidekeys
  logging enable

! --- Logging -------------------------------------------------
logging buffered 16384 debug
logging trap notifications
no logging console
logging source-interface Vlan<MGMT_VLAN>
service timestamps debug datetime msec localtime show-timezone
service timestamps log datetime msec localtime show-timezone

! --- NTP, authenticated --------------------------------------
ntp authenticate
ntp authentication-key 1 hmac-sha2-256 <NTP_KEY>
ntp trusted-key 1
ntp server <NTP_SERVER_IP> key 1
ntp source Vlan<MGMT_VLAN>

! --- SNMPv3 ---------------------------------------------------
snmp-server group <SNMP_GROUP> v3 priv
snmp-server user <SNMP_USER> <SNMP_GROUP> v3 auth sha <SNMP_AUTH_KEY> priv aes 128 <SNMP_PRIV_KEY>
snmp-server host <NMS_IP> version 3 priv <SNMP_USER>
snmp-server location <LOCATION>
snmp-server contact <CONTACT>

! --- CDP off, LLDP only if the fleet needs it ----------------
no cdp run
! lldp run

! --- VLANs: manual, no VTP ------------------------------------
vtp mode off

vlan 10
 name USERS

vlan 20
 name VOICE

vlan 40
 name MGMT

vlan 999
 name PARKING

interface Vlan1
 shutdown

interface Vlan<MGMT_VLAN>
 description MGMT SVI
 ip address <MGMT_IP> <MGMT_MASK>
 no shutdown

ip default-gateway <MGMT_GATEWAY>

! --- Spanning tree: rapid, guarded ----------------------------
spanning-tree mode rapid-pvst
spanning-tree portfast default
spanning-tree portfast bpduguard default
spanning-tree loopguard default
!
! Priority is set ONLY on the switch that should be root.
! Core: 4096.  Distribution: 8192.  Access: leave the default 32768.
! Setting it here on an access switch will block the root-guarded
! uplink below, or steal root from the core. See the section above.
! spanning-tree vlan <VLAN_LIST> priority <STP_PRIORITY>

! --- Trunk hygiene --------------------------------------------
vlan dot1q tag native

! --- DHCP snooping + DAI + IP source guard --------------------
ip dhcp snooping
ip dhcp snooping vlan 10,20
no ip dhcp snooping information option

ip arp inspection vlan 10,20
ip arp inspection validate src-mac dst-mac ip

! --- Management plane ACL -------------------------------------
ip access-list standard MGMT-ACL
 permit <MGMT_SUBNET> <WILDCARD>
 deny any log

! --- Lines -----------------------------------------------------
line con 0
 exec-timeout 10 0
 logging synchronous
 login authentication CONSOLE

! the AUX port is a login prompt nobody watches - close it
line aux 0
 no exec
 transport input none

line vty 0 15
 access-class MGMT-ACL in
 exec-timeout 10 0
 login authentication default
 transport input ssh

! --- Uplink: trunk to core -------------------------------------
interface GigabitEthernet1/0/24
 description UPLINK <CORE_SWITCH> <CORE_PORT>
 switchport mode trunk
 switchport trunk native vlan <NATIVE_VLAN>
 switchport trunk allowed vlan 10,20,40
 switchport nonegotiate
 spanning-tree guard root
 udld port aggressive
 ip dhcp snooping trust
 ip arp inspection trust
 no shutdown

! --- Access: voice + data --------------------------------------
interface GigabitEthernet1/0/1
 description <SITE> <PORT_LABEL> VOICE+DATA
 switchport mode access
 switchport access vlan 10
 switchport voice vlan 20
 switchport nonegotiate
 switchport port-security maximum 2
 switchport port-security violation restrict
 switchport port-security mac-address sticky
 storm-control broadcast level 5
 storm-control multicast level 10
 spanning-tree portfast
 spanning-tree bpduguard enable
 ip verify source port-security
 no shutdown

! --- Access: data only -----------------------------------------
interface GigabitEthernet1/0/2
 description <SITE> <PORT_LABEL> DATA
 switchport mode access
 switchport access vlan 10
 switchport nonegotiate
 switchport port-security maximum 1
 switchport port-security violation restrict
 switchport port-security mac-address sticky
 storm-control broadcast level 5
 storm-control multicast level 10
 spanning-tree portfast
 spanning-tree bpduguard enable
 ip verify source port-security
 no shutdown

! --- WAP: trunk, tagged SSIDs, untagged management --------------
interface GigabitEthernet1/0/3
 description WAP <SITE> <WAP_NAME>
 switchport mode trunk
 switchport trunk native vlan 40
 switchport trunk allowed vlan 10,20,40
 switchport nonegotiate
 spanning-tree portfast trunk
 spanning-tree bpduguard enable
 no shutdown

! --- Router uplink: management access only ---------------------
interface GigabitEthernet1/0/4
 description ROUTER <SITE> <ROUTER_NAME> <PORT>
 switchport mode access
 switchport access vlan 40
 switchport nonegotiate
 spanning-tree portfast
 spanning-tree bpduguard enable
 no shutdown

! --- Unused port: dead on arrival ------------------------------
interface GigabitEthernet1/0/23
 description UNUSED
 switchport mode access
 switchport access vlan 999
 switchport nonegotiate
 switchport port-security maximum 1
 switchport port-security violation shutdown
 spanning-tree portfast
 spanning-tree bpduguard enable
 shutdown

end

Deployment notes, in the order they’ll bite you:

  • Leave spanning-tree vlan … priority commented out unless this switch is genuinely the root bridge. It is the one line here that causes an outage by being present rather than absent.
  • Generate the RSA key before anything depends on it: crypto key generate rsa modulus 4096 on the exec line. If the platform caps at 2048, take it and document it.
  • Apply the ip ssh server algorithm lines from a session you can afford to lose, and open a second session to prove they work before you close the first.
  • Create flash:config-archive first (mkdir flash:config-archive), or the archive’s first write fails on some platforms.
  • Test the lockout paths on a lab switch: TACACS server down, local login, console login, command authorization with a non-privileged user.
  • The management VLAN must be on every uplink’s allowed list, or the switch vanishes on first plug-in.
  • Static-IP hosts on user VLANs need binding entries before DAI and IP source guard will pass their traffic.
  • If a device on a native VLAN needs untagged frames, vlan dot1q tag native changes its world. Lab-test the WAPs and phones before the wide rollout.

And the ground rule that made all of this survivable: the template is a floor, not a ceiling. If a site needs an exception, the exception goes through a review, not straight into a config. That is how one template stays the truth for a hundred switches.


Related: Cisco Switchport Modes, DTP, and switchport nonegotiate Explained, Cisco Native VLAN: How It Works and Why Mismatches Bite, Easily Find Unused Ports on a Cisco Switch

More about Mike →

← Previous