Overview
Phishing remains the number-one initial access vector in real-world breaches. Understanding how these attacks are constructed — and testing whether your users can spot them — is a core responsibility for any security team. Gophish is an open-source phishing simulation framework written in Go. It gives you a clean web UI to build campaigns, craft convincing email templates, host credential-capture landing pages, and generate detailed analytics on who clicked what.
This project walks you through deploying Gophish inside Docker Compose with an NGINX reverse proxy for TLS termination, configuring a local SMTP relay, and running a complete simulated phishing campaign against a test group. By the end you'll have a reusable, containerized lab that you can safely run against internal users (with authorization) or against dummy accounts for red-team practice.
Legal & Ethical Notice: Only run phishing simulations against accounts and users you have explicit written authorization to test. Unauthorized phishing testing violates computer fraud laws in most jurisdictions. This lab is intended for authorized security awareness programs, internal red-team engagements, and controlled homelab environments.
Architecture
┌──────────────────────────────────────┐
│ Docker Host │
│ │
Browser / Email │ ┌──────────┐ ┌───────────────┐ │
──────────────────► │ │ NGINX │──► │ Gophish │ │
:443 (phish site) │ │ (TLS) │ │ :3333 admin │ │
:8443 (admin panel) │ │ │──► │ :80 phish │ │
│ └──────────┘ └───────┬───────┘ │
│ │ │
│ ┌───────▼───────┐ │
│ │ SQLite DB │ │
│ │ (volume) │ │
│ └───────────────┘ │
│ │
│ ┌──────────────────────────────┐ │
│ │ Mailhog (local SMTP catcher)│ │
│ │ :1025 SMTP :8025 web UI │ │
│ └──────────────────────────────┘ │
└──────────────────────────────────────┘
Components:
| Container | Role |
|---|---|
gophish | Core platform — campaign management, tracking, analytics |
nginx | Reverse proxy — TLS termination for both admin and phish listeners |
mailhog | Local SMTP trap — catches all outbound emails so nothing escapes the lab |
Using Mailhog as the SMTP relay ensures zero risk of accidentally emailing real people during development. Switch to a real SMTP relay only when you're ready to run an authorized live campaign.
Prerequisites
- Docker Engine 24+ and Docker Compose v2
- A Linux host (or WSL2 on Windows)
- OpenSSL for generating self-signed certificates
- A domain or
/etc/hostsentry for your phishing domain (e.g.phish.lab.local)
Step 1 — Project Structure
Create the working directory:
mkdir -p ~/gophish-lab/{nginx,certs,gophish-data}
cd ~/gophish-labYour final layout will look like this:
gophish-lab/
├── docker-compose.yml
├── nginx/
│ └── nginx.conf
├── certs/
│ ├── admin.crt
│ ├── admin.key
│ ├── phish.crt
│ └── phish.key
└── gophish-data/ # auto-created by Docker volume
Step 2 — Generate TLS Certificates
Gophish needs separate TLS certificates for the admin panel and the phishing listener. In a real engagement you'd use Let's Encrypt; for the lab, self-signed certs are fine.
cd ~/gophish-lab/certs
# Admin panel cert (CN = localhost)
openssl req -x509 -newkey rsa:4096 -keyout admin.key -out admin.crt \
-days 365 -nodes \
-subj "/C=CA/ST=AB/L=Lab/O=CosmicBytez/CN=localhost"
# Phishing domain cert (CN = phish.lab.local)
openssl req -x509 -newkey rsa:4096 -keyout phish.key -out phish.crt \
-days 365 -nodes \
-subj "/C=CA/ST=AB/L=Lab/O=CosmicBytez/CN=phish.lab.local"
chmod 600 *.keyStep 3 — Gophish Configuration
Create a custom config.json that Gophish will use at startup. Save this as gophish-data/config.json:
{
"admin_server": {
"listen_url": "0.0.0.0:3333",
"use_tls": true,
"cert_path": "/etc/gophish/certs/admin.crt",
"key_path": "/etc/gophish/certs/admin.key"
},
"phish_server": {
"listen_url": "0.0.0.0:80",
"use_tls": false,
"cert_path": "",
"key_path": ""
},
"db_name": "sqlite3",
"db_path": "/opt/gophish/gophish.db",
"migrations_prefix": "db/db_",
"contact_address": "",
"logging": {
"filename": "",
"level": ""
}
}We're terminating TLS at NGINX, so the Gophish phish listener runs plain HTTP internally. NGINX handles HTTPS on port 443 and proxies to Gophish:80.
Step 4 — NGINX Configuration
Create nginx/nginx.conf:
events {
worker_connections 1024;
}
http {
# ── Admin panel (port 8443 → Gophish :3333) ──────────────────────
server {
listen 8443 ssl;
server_name localhost;
ssl_certificate /etc/nginx/certs/admin.crt;
ssl_certificate_key /etc/nginx/certs/admin.key;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
location / {
proxy_pass https://gophish:3333;
proxy_ssl_verify off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https;
}
}
# ── Phishing site (port 443 → Gophish :80) ───────────────────────
server {
listen 443 ssl;
server_name phish.lab.local;
ssl_certificate /etc/nginx/certs/phish.crt;
ssl_certificate_key /etc/nginx/certs/phish.key;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
location / {
proxy_pass http://gophish:80;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https;
}
}
# ── HTTP redirect ─────────────────────────────────────────────────
server {
listen 80;
return 301 https://$host$request_uri;
}
}Step 5 — Docker Compose
Create docker-compose.yml:
services:
gophish:
image: gophish/gophish:latest
container_name: gophish
restart: unless-stopped
volumes:
- ./gophish-data:/opt/gophish
- ./certs:/etc/gophish/certs:ro
networks:
- phishnet
# Internal ports only — NGINX handles external exposure
expose:
- "3333"
- "80"
nginx:
image: nginx:alpine
container_name: gophish-nginx
restart: unless-stopped
ports:
- "443:443" # Phishing site (HTTPS)
- "8443:8443" # Admin panel (HTTPS)
- "8080:80" # HTTP → HTTPS redirect
volumes:
- ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
- ./certs:/etc/nginx/certs:ro
depends_on:
- gophish
networks:
- phishnet
mailhog:
image: mailhog/mailhog:latest
container_name: gophish-mailhog
restart: unless-stopped
ports:
- "8025:8025" # Mailhog web UI
# SMTP port 1025 is internal only
expose:
- "1025"
networks:
- phishnet
networks:
phishnet:
driver: bridgeStep 6 — Launch the Stack
cd ~/gophish-lab
docker compose up -dWatch the Gophish logs for the initial admin credentials:
docker logs gophish 2>&1 | grep -A2 "Please login"
# Output example:
# time="2026-07-08T..." level=info msg="Please login with the username admin and the password <RANDOM_PASSWORD>"Copy that password — you'll need it for the first login. After logging in, you'll be prompted to change it.
Step 7 — Add a Local DNS Entry
So your browser resolves the phishing domain correctly:
# On the Docker host (and any test machines):
sudo tee -a /etc/hosts <<EOF
127.0.0.1 phish.lab.local
EOFStep 8 — First Login & Admin Setup
- Open
https://localhost:8443in your browser (accept the self-signed cert warning) - Log in with
adminand the password from Step 6 - Set a strong password when prompted
- You'll land on the Gophish dashboard
Step 9 — Configure the Sending Profile (SMTP)
Sending Profiles tell Gophish how to deliver emails.
- Navigate to Sending Profiles → New Profile
- Fill in:
- Name:
Lab Mailhog - From:
IT Security <security@phish.lab.local> - Host:
mailhog:1025 - Username / Password: leave blank (Mailhog accepts unauthenticated)
- Name:
- Click Send Test Email — enter your own address and verify it appears in the Mailhog UI at
http://localhost:8025 - Save the profile
Step 10 — Create a User Group (Target List)
- Navigate to Users & Groups → New Group
- Name it
Lab Users - Add test targets manually or import via CSV:
First Name,Last Name,Email,Position
Alice,Smith,alice@lab.local,Analyst
Bob,Jones,bob@lab.local,Developer
Carol,White,carol@lab.local,Manager- Save the group
Step 11 — Build an Email Template
We'll simulate a password reset notification — a classic phishing lure.
- Navigate to Email Templates → New Template
- Name:
Password Reset - IT - Subject:
[Action Required] Your Password Will Expire in 24 Hours - Switch to HTML tab and paste:
<!DOCTYPE html>
<html>
<head><meta charset="UTF-8"></head>
<body style="font-family:Arial,sans-serif;color:#333;max-width:600px;margin:0 auto;padding:20px;">
<div style="border-bottom:3px solid #0066cc;padding-bottom:10px;margin-bottom:20px;">
<strong style="font-size:18px;">CosmicBytez IT Security</strong>
</div>
<p>Hello {{.FirstName}},</p>
<p>Our systems have detected that your account password will expire in <strong>24 hours</strong>.
To avoid losing access to your account, please click the button below to reset your password immediately.</p>
<div style="text-align:center;margin:30px 0;">
<a href="{{.URL}}" style="background:#0066cc;color:white;padding:12px 28px;text-decoration:none;border-radius:4px;font-weight:bold;">
Reset My Password
</a>
</div>
<p style="font-size:12px;color:#999;">
If you did not request this email, please contact the IT Help Desk immediately.<br>
This link expires in 24 hours.
</p>
<hr style="border:none;border-top:1px solid #eee;margin-top:30px;">
<p style="font-size:11px;color:#bbb;">IT Security Team — CosmicBytez Labs</p>
</body>
</html>
{{.FirstName}}and{{.URL}}are Gophish template variables.{{.URL}}automatically injects the tracking link for each recipient.
- Check Add Tracking Image to track email opens
- Save the template
Step 12 — Build a Landing Page
The landing page is what users see after clicking the link. We'll capture submitted credentials.
- Navigate to Landing Pages → New Page
- Name:
Password Reset Form - Click Import Site and paste this HTML:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Password Reset — CosmicBytez IT</title>
<style>
body { font-family: Arial, sans-serif; background: #f0f2f5; display: flex; justify-content: center; align-items: center; height: 100vh; margin: 0; }
.card { background: white; border-radius: 8px; padding: 40px; width: 360px; box-shadow: 0 4px 12px rgba(0,0,0,0.1); }
h2 { color: #0066cc; margin-top: 0; }
label { font-size: 13px; color: #555; display: block; margin-top: 14px; }
input { width: 100%; padding: 10px; margin-top: 4px; border: 1px solid #ccc; border-radius: 4px; box-sizing: border-box; }
button { width: 100%; padding: 12px; margin-top: 20px; background: #0066cc; color: white; border: none; border-radius: 4px; font-size: 15px; cursor: pointer; }
.note { font-size: 11px; color: #999; margin-top: 16px; text-align: center; }
</style>
</head>
<body>
<div class="card">
<h2>Reset Your Password</h2>
<p style="font-size:13px;color:#666;">Enter your current credentials to verify your identity and set a new password.</p>
<form method="POST" action="">
<label>Email Address</label>
<input type="email" name="email" placeholder="you@cosmicbytez.ca" required>
<label>Current Password</label>
<input type="password" name="password" placeholder="••••••••" required>
<label>New Password</label>
<input type="password" name="new_password" placeholder="••••••••" required>
<button type="submit">Reset Password</button>
</form>
<p class="note">Secured by IT Security · CosmicBytez Labs</p>
</div>
</body>
</html>- Enable Capture Submitted Data and Capture Passwords (for the lab)
- Set Redirect to
https://www.google.com(to redirect after form submit — users won't immediately know they've been phished) - Save the page
Step 13 — Launch a Campaign
- Navigate to Campaigns → New Campaign
- Fill in:
- Name:
Lab Phish Test 01 - Email Template:
Password Reset - IT - Landing Page:
Password Reset Form - URL:
https://phish.lab.local(your phishing domain) - Launch Date: now (or schedule it)
- Sending Profile:
Lab Mailhog - Groups:
Lab Users
- Name:
- Click Launch Campaign
Gophish immediately queues emails. Check Mailhog (http://localhost:8025) to confirm they arrived.
Testing the Campaign
Simulate a user clicking the link
- Open Mailhog at
http://localhost:8025 - Click an email from the campaign
- Click the Reset My Password button in the email body
- You'll be redirected to
https://phish.lab.local(accept the cert warning) - Submit the fake credential form
- Observe the redirect to Google
Check campaign results
Back in the Gophish dashboard under Campaigns, click your campaign name:
| Event | What it means |
|---|---|
| Email Sent | Gophish delivered the email to SMTP |
| Email Opened | Tracking pixel loaded (email was opened) |
| Clicked Link | User clicked the phishing URL |
| Submitted Data | User filled out and submitted the credential form |
You'll see per-user timeline events and captured credentials (in the lab, these are dummy values).
Verify tracking pixel
# Check Gophish logs for open/click tracking events
docker logs gophish --tail 50 | grep -E "open|click|submit"Step 14 — Export a Results Report
Gophish can export campaign results to CSV:
- Go to Campaigns → [Your Campaign] → Results
- Click the Export CSV button
- The export includes timestamps for each event type per user — useful for measuring campaign effectiveness
Deployment Notes
Running a live authorized campaign
When you're ready to run against real users (with authorization), swap out Mailhog for a real SMTP relay:
// In the Gophish Sending Profile:
{
"Host": "smtp.yourprovider.com:587",
"Username": "your-smtp-user",
"Password": "your-smtp-password"
}Also replace the self-signed certificates with Let's Encrypt certificates for the phishing domain — users are much more likely to interact with a valid HTTPS site:
# On a public-facing server (not the lab):
certbot certonly --standalone -d phish.yourdomain.comData persistence
Campaign data lives in /opt/gophish/gophish.db inside the container, which is mapped to ./gophish-data/ on the host. Backups are simple:
cp ~/gophish-lab/gophish-data/gophish.db ~/gophish-db-backup-$(date +%F).dbRestricting admin panel access
In production, never expose the Gophish admin port publicly. Restrict it to localhost and use an SSH tunnel for remote access:
# On your local machine:
ssh -L 8443:localhost:8443 user@yourserver
# Then browse https://localhost:8443Extensions & Next Steps
1. Spear Phishing Variants
Customize email templates per target role. Gophish supports template variables beyond {{.FirstName}} — you can reference {{.LastName}}, {{.Position}}, and {{.Email}} to personalize each message at scale.
2. SMS Phishing (Smishing) Awareness
Extend awareness training beyond email by using a test SMS gateway (e.g. Twilio in sandbox mode) with custom short links and training users to verify sender identity.
3. Multi-Scenario Campaigns
Build a library of templates covering common lures:
- Shared document notifications (OneDrive/Google Drive spoofs)
- HR policy updates requiring "immediate acknowledgment"
- IT security alerts about suspicious login activity
- Package delivery notifications
4. Integration with Wazuh / SIEM
Forward Gophish campaign events to your SIEM for correlation. If a user clicks a phishing link during a real-world campaign, Wazuh can alert and correlate that event with other IOCs on the same endpoint.
# Tail Gophish access logs and pipe to syslog forwarder
docker logs -f gophish | logger -t gophish5. Phishing Awareness Training Workflow
Build a full cycle:
- Run a baseline phishing campaign (measure susceptibility)
- Identify users who clicked/submitted — enroll in awareness training
- Run a follow-up campaign 30 days later to measure improvement
- Report metrics to management: click rates before/after training
6. GoReport — Automated PDF Reports
GoReport is a Python tool that pulls Gophish campaign data via the API and generates formatted PDF reports — useful for delivering results to stakeholders.
pip install goreport
goreport --gophish-api-key <your-api-key> --campaign-id 1 --report-format pdfTroubleshooting
| Issue | Fix |
|---|---|
| Gophish container exits on start | Check docker logs gophish — often a malformed config.json or missing cert files |
| Can't reach admin panel | Confirm NGINX is running: docker ps. Try curl -k https://localhost:8443 |
| Emails not appearing in Mailhog | Verify Gophish sending profile host is mailhog:1025 (container DNS name) |
| Phish site cert warning | Expected — self-signed certs trigger browser warnings. Accept or install cert into trusted store |
| Tracking pixels not registering | Some email clients block remote images. Email opens will show as 0 for those clients |
| Campaign link redirects to wrong URL | Verify the URL field in the campaign matches your NGINX phish domain exactly |
Summary
You now have a fully functional, containerized Gophish phishing simulation lab with:
- Gophish for campaign management, tracking, and analytics
- NGINX for TLS termination and port separation
- Mailhog for safe local email capture during development
This setup gives you everything you need to conduct authorized security awareness assessments, measure user susceptibility over time, and build a data-driven training program. Swap Mailhog for a live SMTP relay and self-signed certs for Let's Encrypt when you're ready to move from lab to production campaigns.