Archives

Archives

Tags

  • Home
  • Networking
  • Creating a nested PfSense VM for an isolated lab on ESXi 8

Creating a nested PfSense VM for an isolated lab on ESXi 8

In this homelab guide, we are going to build an isolated virtual lab behind a pfSense firewall on an ESXi 8 host. You can use this design to simulate a small company network, break things safely, and learn how routing and NAT actually behave in a nested firewall setup.

The goal is simple: a virtual pfSense VM protects a small internal lab network, while the rest of the home network stays separated. Later in the guide, we also add proper routing so a laptop on another subnet can reach the lab without hiding everything behind NAT.

What you will build
A virtual pfSense firewall with a WAN side connected to your existing network and a LAN side connected to isolated lab VMs. The lab will get internet access, and in the advanced section we will make traffic between your laptop and the lab route cleanly without source IP translation.

Lab overview

ComponentExample in this labPurpose
ESXi hostESXi 8.xRuns the virtual firewall and lab VMs
Virtual firewallpfSense 2.7.2 ISO, updated to 2.8.xRoutes and filters traffic for the isolated lab
Lab VMWindows Server 2025 / DC01First machine inside the isolated network
WAN port groupDP_WAN_PFConnects pfSense WAN to the existing physical network
LAN port groupDP_VM_LANConnects pfSense LAN and lab VMs together

IP plan used in the screenshots

NetworkExampleNotes
Transit network between physical pfSense and virtual pfSense192.168.5.0/24The virtual pfSense WAN lives here. In my screenshots the virtual pfSense WAN is 192.168.5.5.
Virtual lab LAN192.168.23.0/24The virtual pfSense LAN is 192.168.23.1. DC01 lives in this subnet.
Laptop / Wi-Fi VLAN192.168.100.0/24Used later in the advanced routing section. My laptop is 192.168.100.26.

Use your own IP ranges
The exact IP addresses do not matter. What matters is the design: the virtual pfSense WAN is reachable by your existing firewall, and the virtual pfSense LAN is a separate subnet used only by the lab VMs.

Physical topology

This is the physical side of the setup. The ESXi host is connected to the existing network, and the virtual pfSense WAN will use that path to reach the rest of the homelab.


Virtual topology

Inside ESXi, the design is intentionally simple. We use one vSwitch for the pfSense WAN and one isolated vSwitch for the lab LAN. You could make this more advanced with VLANs, a DMZ, multiple domain controllers, or separate server networks, but this guide keeps the first build clean and understandable.


Step 1 — Create the ESXi networking

Before creating any VMs, we need the virtual networking to be correct. The pfSense VM will have two network adapters: one connected to the WAN side and one connected to the lab LAN side.

Create vSwitch1 for the pfSense WAN

The first standard switch is for the WAN side of the virtual pfSense. This vSwitch should use the physical uplink that connects toward your existing firewall or upstream network. In my lab this is vmnic3 which is connected to the physical firewall.

Security idea
Treat this port group as the “outside” of your virtual firewall. Do not casually connect normal lab servers directly to this network unless you specifically want them outside the virtual firewall.

Go to the Networking Menu and under Virtual switches, Add a standard virtual switch:

Give the vSwitch a name, choose the uplink (physical interface that is connected to the physical firewall) and click ADD:

Create vSwitch2 for the isolated VM LAN

The second standard switch is for the lab LAN. This is where the pfSense LAN interface and the internal lab VMs will connect. I do not add a physical uplink to this vSwitch because all lab VMs are on the same ESXi host. That keeps the lab isolated.

We give the vSwitch a name, remove the Uplink 1 and click ADD:

Create the port groups

Now create two port groups. The port group is what you actually select on a VM network adapter.

Port groupAttached vSwitchUsed by
DP_WAN_PFvSwitch1 / WAN_SUBNETpfSense WAN adapter
DP_VM_LANvSwitch2 / VM_LANpfSense LAN adapter and internal lab VMs

For the first Port Group, we give it a name and choose the Virtual Switch it’s connected to:

We do the same for the second Port Group:

Checkpoint
At this point the ESXi network foundation is ready. You should have one WAN-side port group and one isolated LAN-side port group.


Step 2 — Create the pfSense VM

Next we create the pfSense virtual machine. This VM becomes the firewall and default gateway for the isolated lab.

We enter PFSENSE01, choose the default compatibility and “Other” – “FreeBSD 14” as the Guest OS family & version:

Using Slow (SAS HDD) storage is okay for the hard drive of a pfsense:

Recommended VM settings

SettingValue
NamePFSENSE01
Guest OS family / versionOther / FreeBSD 14
vCPU2
Memory2 GB
Disk8 GB
SCSI controllerLSI Logic SAS
Network adapter 1DP_WAN_PF
Network adapter 2DP_VM_LAN
Adapter typeVMXNET3
CD/DVDpfSense 2.7.2 ISO from datastore

Storage note
pfSense does not need fast storage for a small lab firewall. Slow SAS HDD storage is fine for this VM unless you are doing heavier logging, packages, or high-throughput testing.

We also need to add another network adapter since our pfSense has a WAN and a LAN:


Step 3 — Install pfSense

Power on the pfSense VM and open the console. The installer is straightforward: accept the license, follow the default installation flow, and reboot when it finishes.

When the installation is done, eject the ISO from the virtual CD/DVD drive and reboot the VM.

Map the correct WAN and LAN interfaces

After the reboot, pfSense asks you to assign interfaces. This part matters. In ESXi, check the MAC addresses of the VM adapters so you know which one is connected to DP_WAN_PF and which one is connected to DP_VM_LAN.

In my case, vmx0 is the WAN interface and vmx1 is the LAN interface. I do not configure VLANs in this first build, so I answer “no” to the VLAN question and enter the WAN-interface

For the LAN I do the same and proceed:

Assign static IP addresses

By default, pfSense may try to use DHCP on WAN. I prefer a static WAN IP for lab firewalls because it makes routes, firewall rules, and troubleshooting much easier. Use option 2 in the console menu to configure the interfaces.

For the WAN side, I assign an IP in the transit network. In my case the virtual pfSense WAN is 192.168.5.5, and the upstream gateway is the physical pfSense interface in that same network.

I enter the IP, subnet mask as bit counts, the upstream gateway (interface of my phsyical firewall) and set it as default:

I also change the default LAN network. The default pfSense LAN is very common and may overlap with other home or ISP networks, so I use 192.168.23.1/24 for the lab LAN.

Checkpoint
The virtual pfSense now has a WAN interface on the existing network and a LAN interface for the isolated lab. The next problem is that we still need a VM inside the lab LAN to access the pfSense web GUI.


Step 4 — Create the first Windows Server VM

To finish pfSense from the web interface, we need a machine on the lab LAN. I create a Windows Server 2025 VM called DC01 and connect it to the DP_VM_LAN port group.

Lab-only shortcut
In this build I disabled Secure Boot to keep the lab simple. That is not a security recommendation for production. For real environments, keep security features enabled unless you have a specific reason to disable them.

I will not cover the entire Windows Server installation here. The important part for this guide is that the VM network adapter is connected to the lab LAN port group and that Windows gets an IP address in the pfSense LAN subnet.

Set a static IP on DC01

After Windows is installed, configure a static IP address in the pfSense LAN range. The default gateway should be the pfSense LAN IP. In this lab that is 192.168.23.1.


Step 5 — Finish the pfSense web configuration

From DC01, open a browser and go to the pfSense LAN IP. In this lab the URL is:

https://192.168.23.1

Default login
The default username is admin and the default password is pfsense

pfSense starts a setup wizard. You can use the wizard or skip it and configure the basics manually. For this lab I go through the important settings directly:

DNS and time settings

I disable the option that lets the WAN override my DNS settings, because I want full control over what DNS servers the lab uses. In my setup, the primary DNS server is my physical firewall, but you can also use your ISP DNS or public resolvers:

Set a correct NTP server as well. Accurate time is important when troubleshooting logs, firewall states, certificate issues, authentication issues, and domain controller behavior:

WAN settings and DNS behavior

For the rest of the WAN settings, I leave the defaults unless there is a specific reason to change them.

For this lab, I also configure pfSense so clients use the remote DNS servers instead of relying on pfSense itself as the DNS resolver.

Test internet access from DC01

After saving the settings, DC01 should have internet access through the virtual pfSense. I verify this with ping and nslookup.

Update pfSense

Once the basic configuration works, update pfSense to the 2.8.x branch from the web interface.

After the reboot, log back in and confirm that the version is now 2.8.x.

Installing VMware Tools on the pfSense

Since we’re in the VMware eco-system, our pfSense will run optimally with the VMware Tools installed:

Basic build complete
At this point you have a working isolated lab behind a virtual pfSense firewall. The lab VM can reach the internet, and the lab LAN is separated from the rest of your home network.


Advanced part — Access the lab from your laptop without ugly NAT

The basic lab works, but now comes the interesting part. I want to RDP or ping from my laptop to DC01 inside the lab. I do not want everything to appear as the virtual pfSense WAN IP. I want the real source IPs to stay visible.

The goal is clean routing: Laptop01 should see DC01 as DC01, and DC01 should see Laptop01 as Laptop01.

Allow private networks on the virtual pfSense WAN

By default, pfSense blocks private networks on WAN. That makes sense for a normal internet-facing firewall, but in a nested homelab the WAN of the virtual pfSense is also a private RFC1918 network. So we need to allow private networks on the virtual pfSense WAN interface.

On PFSENSE01, I click Interfaces & WAN:

Scroll down and uncheck the boxes that prevent private networks from passing through the WAN

Homelab-only note
Only uncheck “Block private networks” when your WAN is intentionally connected to another private/internal network, like this nested pfSense lab. Do not blindly copy this setting to a firewall directly connected to the internet.

Allow ICMP where needed

For testing, I use ICMP/ping. That means the firewalls and Windows hosts must allow ICMP echo traffic.

  • On the virtual pfSense: allow ICMP on the interfaces involved in the test.
  • On the physical pfSense: allow ICMP between the laptop VLAN and the transit network/lab route where needed.
  • On Windows: enable the Windows Defender Firewall rule that allows ICMP echo requests. This one is easy to forget.

Under Firewall -> RULES -> LAN -> We add a new rule that allows ICMP:

Repeat the process on all interfaces between the VM and the laptop.

And on VM and Laptop, we open the Defender Firewall GUI and enable the ICMP rule:


Routing & NAT : Traffic between VM and Laptop

Test 1: DC01 to Laptop01

When I ping from DC01 to Laptop01, the ping works. At first this looks perfect.

But when I check with Wireshark on the laptop, the source IP is not DC01. The laptop sees the virtual pfSense WAN IP instead.

What happened?
DC01 got NAT’ted!

The traffic started on the virtual pfSense LAN and left through the virtual pfSense WAN. With automatic outbound NAT enabled, pfSense translated the source from the DC01 IP to the virtual pfSense WAN IP, 192.168.5.5.

Why did the PING function without touching anything?

DC01 sends an ICMP echo request to Laptop01:

192.168.23.2 → 192.168.100.26

DC01 uses its default gateway, which is the LAN interface of the virtual pfSense:

192.168.23.1

When the packet passes through the virtual pfSense, pfSense creates a state entry for the connection. Because outbound NAT is enabled by default, the source IP is translated when the packet leaves the virtual pfSense WAN interface.

So the packet changes from:

192.168.23.2 → 192.168.100.26

to:

192.168.5.5 → 192.168.100.26

Now the physical pfSense receives a packet from 192.168.5.5 to 192.168.100.26. This is easy for the physical pfSense, because both networks are known/directly connected or routed in its routing table.

Laptop01 receives the ping from 192.168.5.5 and replies to 192.168.5.5.

The reply goes back to the virtual pfSense. The virtual pfSense checks its state/NAT table and knows that this reply belongs to the original DC01 connection. It then translates the destination back to 192.168.23.2 and forwards it to DC01.

That is why DC01 → Laptop01 works without adding any static route on the physical pfSense.


Test 2: Laptop01 to DC01 fails

Now I try the opposite direction: pinging from Laptop01 to DC01. This fails.

A traceroute shows the real issue: the physical pfSense does not know where 192.168.23.0/24 is located, so it sends the packet toward the default gateway, which is the ISP (and hereafter: the internet).

What happened?
Routing failed!
The traffic started on the virtual pfSense LAN and left through the virtual pfSense WAN. With automatic outbound NAT enabled, pfSense translated the source from the DC01 IP to the virtual pfSense WAN IP, 192.168.5.5.

Why did the PING fail?

Laptop01 sends:

192.168.100.26 → 192.168.23.2

Laptop01 uses its default gateway:

192.168.100.1

The physical pfSense now has to route traffic to 192.168.23.0/24. But it does not know this network!

Unless we add a static route, the physical pfSense does not know that 192.168.23.0/24 is behind 192.168.5.5.

So the physical pfSense sends the packet to its default gateway/WAN instead of sending it to the virtual pfSense.

Conclusion -> Routing problem
The physical firewall needs a route that says: 192.168.23.0/24 is reachable via 192.168.5.5, which is the WAN IP of the virtual pfSense.


Step 6 — Add a static route on the physical pfSense

On pfSense, a static route uses a gateway object. So first we create a gateway that points to the virtual pfSense WAN IP.

FieldValue in this lab
InterfaceThe physical pfSense interface where 192.168.5.5 is reachable
Gateway IP192.168.5.5
DescriptionVirtual pfSense lab gateway
Monitor IPDisabled in my lab to avoid extra ping noise during troubleshooting

After the gateway exists, add the static route.

Destination networkGateway
192.168.23.0/24192.168.5.5

Test again from Laptop01 to DC01

Now the ping from Laptop01 to DC01 works.

The traceroute also looks correct now. The laptop sends traffic to its default gateway, and the physical pfSense forwards the lab subnet traffic to the virtual pfSense.

When I check Wireshark on DC01, the original laptop IP is preserved. That means laptop-to-DC traffic is routed, not NATed.

Important distinction
The static route fixes reachability from the laptop side to the lab subnet. It does not automatically disable outbound NAT for new traffic initiated from DC01 to the laptop.

The laptop-to-DC ping now works because the physical pfSense has a static route for 192.168.23.0/24 via 192.168.5.5.

The packet does not get NATed. The physical pfSense only routes the packet to the next hop, which is the WAN interface of the virtual pfSense. The virtual pfSense then routes it from WAN to LAN toward DC01.

Because no NAT rule is translating this traffic, DC01 sees the original source IP of the laptop: 192.168.100.26.

However, this does not automatically mean that new traffic initiated from DC01 to Laptop01 is no longer NATed. If the virtual pfSense still has automatic outbound NAT enabled, new traffic from 192.168.23.0/24 going out of the virtual pfSense WAN can still be translated to 192.168.5.5.

So:
Laptop01 → DC01 = routed, no NAT.
DC01 → Laptop01 = still NATed unless we add a no-NAT outbound rule or switch outbound NAT to manual/hybrid with the correct no-NAT rule.


We’re not done yet! As I want the DC01’s original IP to be used and not a NAT-address. Let’s fix this:

Step 7 — Disable NAT from the lab subnet to the laptop subnet

We still have one remaining problem. New traffic started by DC01 toward Laptop01 can still be NATed by the virtual pfSense. That hides the DC01 IP behind 192.168.5.5.

To fix that, we create a no-NAT rule on the virtual pfSense for traffic from the lab subnet to the laptop subnet.

SettingValue
pfSense deviceVirtual pfSense
MenuFirewall → NAT → Outbound
ModeHybrid or Manual outbound NAT
Source192.168.23.0/24
Destination192.168.100.0/24
TranslationNone / Do not NAT
Rule orderPlace above general outbound NAT rules

After saving and applying the rule, I ping from DC01 to Laptop01 again. This time Wireshark on the laptop shows the real DC01 source IP instead of the virtual pfSense WAN IP.

Final result
Both directions now behave cleanly. Laptop01 can reach DC01 through the static route, and DC01 can reach Laptop01 without being hidden behind NAT.


What actually happened?

This lab is a great example of the difference between routing and NAT.

Laptop01 → DC01

Laptop01 192.168.100.26 → DC01 192.168.23.x

The laptop sends the packet to its default gateway. The physical pfSense uses the static route for 192.168.23.0/24 and forwards the packet to 192.168.5.5, which is the virtual pfSense WAN. The virtual pfSense then routes it to the LAN side. No NAT is needed in this direction.

DC01 → Laptop01

DC01 192.168.23.x → Laptop01 192.168.100.26

This starts from the virtual pfSense LAN and leaves the virtual pfSense WAN. By default, pfSense outbound NAT may translate the source to 192.168.5.5. The no-NAT rule prevents that for traffic going to the laptop subnet.

The clean design

Static route on physical pfSense:
192.168.23.0/24 via 192.168.5.5

No-NAT rule on virtual pfSense:
Source:      192.168.23.0/24
Destination: 192.168.100.0/24
Translation: none

Troubleshooting checklist

  • Can DC01 ping the virtual pfSense LAN IP?
  • Can the virtual pfSense ping its upstream gateway?
  • Can DC01 resolve DNS names with nslookup?
  • Is “Block private networks” disabled on the virtual pfSense WAN?
  • Does the physical pfSense have a static route to 192.168.23.0/24 via 192.168.5.5?
  • Are firewall rules allowing ICMP or RDP on the correct incoming interface?
  • Does Windows Defender Firewall allow ICMP/RDP on DC01?
  • Is the no-NAT rule above the general outbound NAT rules?
  • Does Wireshark show the real source IP or the translated pfSense WAN IP?

Conclusion

That is the full build: an isolated pfSense-powered homelab running on ESXi, with a Windows Server VM behind it, internet access working, and clean routed access from the rest of the home network.

The most important lesson is that “ping works” does not always mean the network is designed correctly. NAT can hide problems. Once you understand where routes and NAT rules are applied, the topology becomes much easier to troubleshoot.

Homelab takeaway
Use NAT when you only need outbound internet access. Use routing when you want clean two-way access and want the real source IPs to remain visible.