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
| Component | Example in this lab | Purpose |
|---|---|---|
| ESXi host | ESXi 8.x | Runs the virtual firewall and lab VMs |
| Virtual firewall | pfSense 2.7.2 ISO, updated to 2.8.x | Routes and filters traffic for the isolated lab |
| Lab VM | Windows Server 2025 / DC01 | First machine inside the isolated network |
| WAN port group | DP_WAN_PF | Connects pfSense WAN to the existing physical network |
| LAN port group | DP_VM_LAN | Connects pfSense LAN and lab VMs together |
IP plan used in the screenshots
| Network | Example | Notes |
|---|---|---|
| Transit network between physical pfSense and virtual pfSense | 192.168.5.0/24 | The virtual pfSense WAN lives here. In my screenshots the virtual pfSense WAN is 192.168.5.5. |
| Virtual lab LAN | 192.168.23.0/24 | The virtual pfSense LAN is 192.168.23.1. DC01 lives in this subnet. |
| Laptop / Wi-Fi VLAN | 192.168.100.0/24 | Used 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 group | Attached vSwitch | Used by |
|---|---|---|
| DP_WAN_PF | vSwitch1 / WAN_SUBNET | pfSense WAN adapter |
| DP_VM_LAN | vSwitch2 / VM_LAN | pfSense 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
| Setting | Value |
|---|---|
| Name | PFSENSE01 |
| Guest OS family / version | Other / FreeBSD 14 |
| vCPU | 2 |
| Memory | 2 GB |
| Disk | 8 GB |
| SCSI controller | LSI Logic SAS |
| Network adapter 1 | DP_WAN_PF |
| Network adapter 2 | DP_VM_LAN |
| Adapter type | VMXNET3 |
| CD/DVD | pfSense 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.






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.
| Field | Value in this lab |
|---|---|
| Interface | The physical pfSense interface where 192.168.5.5 is reachable |
| Gateway IP | 192.168.5.5 |
| Description | Virtual pfSense lab gateway |
| Monitor IP | Disabled in my lab to avoid extra ping noise during troubleshooting |



After the gateway exists, add the static route.
| Destination network | Gateway |
|---|---|
| 192.168.23.0/24 | 192.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.
| Setting | Value |
|---|---|
| pfSense device | Virtual pfSense |
| Menu | Firewall → NAT → Outbound |
| Mode | Hybrid or Manual outbound NAT |
| Source | 192.168.23.0/24 |
| Destination | 192.168.100.0/24 |
| Translation | None / Do not NAT |
| Rule order | Place 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/24via192.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.

