NetPilot
Back to Blog
Tutorial••Updated •11 min

JNCIA Practice Lab: Junos Exercises Without Hardware

A JNCIA-Junos practice lab on two free vJunos routers: candidate config, commit, rollback, static routes, OSPF, routing policy and firewall filters, with real output mapped to the JN0-106 objectives.

S
Sarah Chen
Network Engineer

A JNCIA practice lab needs a Junos device to configure, something to route between, and a way to prove each step. Two vJunos routers give you all three for free. This lab starts with addresses only, and you add the rest by hand: commits and rollbacks, static routes, OSPF, a routing policy and a firewall filter that breaks things on purpose.

All output below comes from one real run on vJunos-router 26.2R1.7. The topology is in our public juniper-labs repository.

JNCIA-Junos exam facts

From Juniper's certification page:

  • Exam: JN0-106, 65 multiple-choice questions, 90 minutes, delivered by Pearson VUE
  • Software version: Junos OS 21.2
  • Prerequisite: none
  • Validity: three years
  • Role: the baseline for the Enterprise and Service Provider routing and switching tracks, so JNCIS comes after it

JN0-105 ended on April 5, 2026, so a guide that names it is out of date. The exam is written, so this lab is practice for understanding, never a copy of the test. It also matters where you get your hands-on time: Juniper's free Open Learning course has recorded lab demonstrations but no labs, and vLabs is being retired.

What this lab covers

JN0-106 objectiveCovered hereTested in this lab
Networking Fundamentals⚠️ In passingAddressing, longest match
Junos OS Fundamentals✅ YesRouting table against forwarding table
User Interfaces✅ MostCLI modes, candidate and active config, compare, rollback
Configuration Basics⚠️ PartInitial addressing, interfaces, rescue config
Operational Monitoring and Maintenance⚠️ PartShow commands, ping, traceroute
Routing Fundamentals✅ YesRoute preference, static routes, OSPF
Routing Policy and Firewall Filters✅ YesExport policy, filter terms, counters, implicit discard

Not tested in this run: J-Web, user accounts and login classes, configuration groups, NTP and syslog, software upgrades, root password recovery and unicast RPF.

The lab

h1 --- ge-0/0/1 [r1] ge-0/0/0 --- ge-0/0/0 [r2] ge-0/0/1 --- h2
10.1.1.10      10.1.1.1  10.0.12.1/30  10.0.12.2  10.2.2.1   10.2.2.10

Two routers and two Linux hosts. Loopbacks are 10.255.0.1 and 10.255.0.2. The startup config holds addresses and nothing else.

On NetPilot, upload the vJunos-router image under Images once, then ask for it:

Build two vJunos routers joined by one link with a Linux host behind each. Give them addresses only. I will configure routing myself by hand.

The agent writes the topology and addresses, deploys, and waits out the boot. To run it yourself, clone the repository and follow Run vJunos in containerlab.

Budget time before you start. Each router boots a VM inside a VM. Ours took 31 to 37 minutes to read Online, on a VM that was also running other labs, and about 18 minutes is what we have measured for one to three nodes in earlier runs. Start the lab, then go read a chapter.

Exercise 1: candidate, compare, commit, rollback

Junos never changes the running device while you type. You edit a candidate configuration, then commit it. Try it with an interface description:

admin@r2> configure
Entering configuration mode
 
[edit]
admin@r2# set interfaces ge-0/0/0 description to-r1
 
[edit]
admin@r2# show | compare
[edit interfaces ge-0/0/0]
+   description to-r1;
 
[edit]
admin@r2# commit check
configuration check succeeds
 
[edit]
admin@r2# commit comment "describe r1 link"
commit complete

Now undo it. rollback 1 loads the previous commit into the candidate. It changes nothing until you commit:

[edit]
admin@r2# rollback 1
load complete
 
[edit]
admin@r2# show | compare
[edit interfaces ge-0/0/0]
-   description to-r1;
 
[edit]
admin@r2# commit and-quit
commit complete
Exiting configuration mode

Read the commit history in operational mode:

admin@r2> show system commit | match "^[0-2] "
0   2026-10-30 21:09:26 UTC by admin via cli
1   2026-10-30 21:07:55 UTC by admin via cli
2   2026-10-30 20:53:44 UTC by root via other

Line 2 is the startup config, loaded at boot. A question worth knowing: operational commands fail inside configuration mode. show configuration interfaces ge-0/0/0 at the [edit] prompt gives syntax error. Prefix it with run, or use show interfaces ge-0/0/0, which reads the candidate.

Exercise 2: static routes

r1 has no route to r2's LAN yet, so show route 10.2.2.0/24 prints nothing. Add one in each direction, with the loopbacks:

admin@r1> configure
admin@r1# set routing-options static route 10.2.2.0/24 next-hop 10.0.12.2
admin@r1# set routing-options static route 10.255.0.2/32 next-hop 10.0.12.2
admin@r1# commit and-quit

On r2, use 10.1.1.0/24 and 10.255.0.1/32 with next hop 10.0.12.1. Then check:

admin@r1> show route protocol static
 
inet.0: 7 destinations, 7 routes (7 active, 0 holddown, 0 hidden)
+ = Active Route, - = Last Active, * = Both
 
10.2.2.0/24        *[Static/5] 00:00:02
                    >  to 10.0.12.2 via ge-0/0/0.0
10.255.0.2/32      *[Static/5] 00:00:02
                    >  to 10.0.12.2 via ge-0/0/0.0

The 5 is the route preference, the Junos word for what Cisco calls administrative distance. Prove it from a host:

$ docker exec clab-jncia-lab-h1 ping -c 3 -i 0.3 10.2.2.10
3 packets transmitted, 3 received, 0% packet loss, time 602ms
 
$ docker exec clab-jncia-lab-h1 traceroute -n -w 1 -q 1 10.2.2.10
 1  10.1.1.1  1.043 ms
 2  10.0.12.2  2.071 ms
 3  10.2.2.10  1.804 ms

Exercise 3: OSPF and route preference

Add OSPF on both routers and leave the static routes in place. The LAN and the loopback are passive, so they are advertised with no neighbor on them:

admin@r1# set protocols ospf area 0.0.0.0 interface ge-0/0/0.0 interface-type p2p
admin@r1# set protocols ospf area 0.0.0.0 interface lo0.0 passive
admin@r1# set protocols ospf area 0.0.0.0 interface ge-0/0/1.0 passive
admin@r1# commit and-quit
admin@r1> show ospf neighbor
Address          Interface              State           ID               Pri  Dead
10.0.12.2        ge-0/0/0.0             Full            10.255.0.2       128    32

Now the routing table holds two routes to the same prefix, and preference picks one:

admin@r1> show route 10.2.2.0/24 exact
 
inet.0: 8 destinations, 10 routes (8 active, 0 holddown, 0 hidden)
+ = Active Route, - = Last Active, * = Both
 
10.2.2.0/24        *[Static/5] 00:02:29
                    >  to 10.0.12.2 via ge-0/0/0.0
                    [OSPF/10] 00:00:02, metric 2
                    >  to 10.0.12.2 via ge-0/0/0.0

The star marks the active route. Static at 5 beats internal OSPF at 10, and the lower number wins.

Exercise 4: routing table and forwarding table

Delete the statics on both routers. OSPF takes over with no change to the path:

admin@r1# delete routing-options static route 10.2.2.0/24
admin@r1# delete routing-options static route 10.255.0.2/32
admin@r1# commit and-quit
admin@r1> show route 10.2.2.0/24 exact
 
inet.0: 8 destinations, 8 routes (8 active, 0 holddown, 0 hidden)
+ = Active Route, - = Last Active, * = Both
 
10.2.2.0/24        *[OSPF/10] 00:00:43, metric 2
                    >  to 10.0.12.2 via ge-0/0/0.0
 
admin@r1> show route forwarding-table destination 10.2.2.0/24 table default
Routing table: default.inet
Internet:
Destination        Type RtRef Next hop           Type Index    NhRef Netif
10.2.2.0/24        user     0 10.0.12.2          ucst      593     4 ge-0/0/0.0

That is the control plane and forwarding plane split the exam asks about. The first command reads the Routing Engine's table of every route it knows. The second reads what was pushed down for forwarding, the winners only.

Exercise 5: an export policy

Take r2's LAN out of OSPF. r1 loses the route, and show route 10.2.2.0/24 exact prints nothing:

admin@r2# delete protocols ospf area 0.0.0.0 interface ge-0/0/1.0
admin@r2# commit and-quit

Bring it back with policy. A direct route does not enter OSPF by itself, so name it and export it:

admin@r2# set policy-options policy-statement export-lan term lan from protocol direct
admin@r2# set policy-options policy-statement export-lan term lan from route-filter 10.2.2.0/24 exact
admin@r2# set policy-options policy-statement export-lan term lan then accept
admin@r2# set protocols ospf export export-lan
admin@r2# commit and-quit
admin@r2> show ospf database external
    OSPF AS SCOPE link state database
 Type       ID               Adv Rtr           Seq      Age  Opt  Cksum  Len
Extern  *10.2.2.0         10.255.0.2       0x80000001     6  0x22 0xe9ae  36

On r1 it now arrives as an external route at preference 150:

admin@r1> show route 10.2.2.0/24 exact
 
inet.0: 8 destinations, 8 routes (8 active, 0 holddown, 0 hidden)
+ = Active Route, - = Last Active, * = Both
 
10.2.2.0/24        *[OSPF/150] 00:00:07, metric 0, tag 0
                    >  to 10.0.12.2 via ge-0/0/0.0

Same prefix, same next hop, different route type. That is the effect of a policy on the routing table, in one line.

Exercise 6: a firewall filter, then break it on purpose

Build a filter that drops and counts ICMP, with a term that accepts the rest. Apply it outbound toward h2:

admin@r2# set firewall family inet filter block-ping term icmp from protocol icmp
admin@r2# set firewall family inet filter block-ping term icmp then count ping-drops
admin@r2# set firewall family inet filter block-ping term icmp then discard
admin@r2# set firewall family inet filter block-ping term rest then accept
admin@r2# set interfaces ge-0/0/1 unit 0 family inet filter output block-ping
admin@r2# commit and-quit
[edit interfaces ge-0/0/1 unit 0 family inet filter]
  'output'
    warning: requires 'L2 and L3 Filters' license
commit complete

Ignore that warning. Juniper's vJunos docs say the license check messages can be ignored, and the filter works:

$ docker exec clab-jncia-lab-h1 ping -c 4 -i 0.3 -W 1 10.2.2.10
4 packets transmitted, 0 received, 100% packet loss, time 912ms
admin@r2> show firewall filter block-ping
 
Filter: block-ping
Counters:
Name                                                                            Bytes              Packets
ping-drops                                                                        336                    4

Four pings, four counted. Now the lesson that sticks. Remove the accept term and put the filter on the OSPF link. A filter ends with an implicit discard, so this drops everything. Use commit confirmed so Junos saves you:

admin@r2# delete firewall family inet filter block-ping term rest
admin@r2# set interfaces ge-0/0/0 unit 0 family inet filter input block-ping
admin@r2# commit confirmed 2 comment "filter with no accept term"
commit confirmed will be automatically rolled back in 2 minutes unless confirmed
commit complete
 
# commit confirmed will be rolled back in 2 minutes

One minute later on r1, the adjacency has fallen and the route is gone:

admin@r1> show ospf neighbor
Address          Interface              State           ID               Pri  Dead
10.0.12.2        ge-0/0/0.0             Init            10.255.0.2       128    35
 
admin@r1> show route 10.2.2.0/24 exact
 

Init means r1 hears r2's hellos, but r2 no longer hears r1, because its inbound filter drops them. Do nothing. Junos reverts by itself:

Broadcast Message from root@r2
        (no tty) at 21:23 UTC...
 
Commit was not confirmed; automatic rollback complete.
 
admin@r2> show system commit | match "^[0-2] "
0   2026-10-30 21:23:15 UTC by root via other
1   2026-10-30 21:19:45 UTC by admin via cli commit confirmed, rollback in 2mins
2   2026-10-30 21:18:02 UTC by admin via cli
 
admin@r2> show ospf neighbor
Address          Interface              State           ID               Pri  Dead
10.0.12.1        ge-0/0/0.0             Full            10.255.0.1       128    38

Asked for two minutes, this rollback landed 3 minutes 30 seconds after the commit. Commits are slow on a busy vJunos and our configuration session was still open, so leave yourself margin. On a real router, commit confirmed is what stops a bad filter from locking you out. If the change is good, run commit inside the window to keep it.

Exercise 7: a rescue configuration

Save a known good config you can load in one step:

admin@r1> request system configuration rescue save
Rescue configuration operation on re0
Success
 
admin@r1> show system configuration rescue | match "host-name|area"
    host-name r1;
        area 0.0.0.0 {

To end, remove the filter from ge-0/0/1 and confirm the hosts can talk:

$ docker exec clab-jncia-lab-h1 ping -c 4 -i 0.3 -W 1 10.2.2.10
4 packets transmitted, 4 received, 0% packet loss, time 902ms

Have the agent drill you

Once the lab runs, the same VM works as a study partner. On NetPilot, ask:

Break one thing in this lab without telling me what, then let me find it from the CLI. Check my answer against the devices.

You stay on the CLI, and the console to each router is always there. To compare notes across vendors, our BGP multi-vendor lab puts Junos next to Cisco and Arista.

After the JNCIA

JNCIA-Junos is the base for JNCIS-ENT and JNCIS-SP. For switching topics such as VLANs, IRB and LACP, you want vJunos-switch rather than vJunos-router. The same two-node shape works. Juniper's own guidance is on the certification page.

FAQ

Is there a lab on the JNCIA-Junos exam?

No. JN0-106 is a written exam with 65 multiple-choice questions in 90 minutes, delivered by Pearson VUE. Hands-on practice still helps, because many objectives ask what a command does or how a configuration behaves.

Can I practice for the JNCIA without Juniper hardware?

Yes. vJunos-router is a free virtual Junos router from Juniper, and two of them cover the CLI, commit model, routing, policy and firewall filter objectives. They need a host with KVM, or a hosted lab. This post runs every exercise on vJunos-router 26.2R1.7.

Does Juniper's free JNCIA course include labs?

No. Juniper's Open Learning FAQ says there are no hands-on labs for that course, only recorded lab demonstrations. Lab access comes with the paid On-Demand course. Juniper vLabs was the usual free option, and a notice on its site says it will be retired on October 31.

What replaced the JN0-105 exam?

JN0-106. Juniper's certification news says JN0-105 reached end of life on April 5, 2026, and the new exam started on April 6, 2026. The certification keeps the JNCIA-Junos name, and the program is now called the HPE Networking Certification Program.

Does a filter with no accept term block everything?

Yes. A Junos firewall filter ends with an implicit discard, so traffic that matches no term is dropped. In this lab a filter with one discard term on the OSPF link took the adjacency down, and commit confirmed rolled it back.

Try NetPilot Free

Build enterprise-grade network labs in seconds with AI assistance

Get Started Free