As part of building my self-hosted Hugo publishing workflow, I ended up with a surprisingly useful reminder:

Even if your deployment looks simple, it often relies on more than one network flow.

This came up when I started tightening firewall rules around my DMZ web server.


The Setup (High Level)

My environment looks roughly like this:

  • A trusted workstation where I write content
  • An internal Git server (Gitea) on the LAN
  • A public-facing web server in a DMZ
  • A deployment script that pulls updates and rebuilds the Hugo site

The publishing workflow is:

  1. Edit content locally
  2. Commit + push to Git
  3. Trigger a deploy command remotely
  4. The web server pulls the latest content and rebuilds the site

The Problem: SSH Worked… Until It Didn’t

Once everything was running smoothly, I decided to lock down SSH access to the DMZ host.

So I created a firewall rule allowing:

Workstation → DMZ Web Server on port 22

That seemed correct.

But suddenly my deploy script (hillpush) failed.

As a test, I temporarily opened all ports — and everything worked again.

That told me immediately:

Another network dependency was being blocked.


The Key Realisation: Deployment Requires Two SSH Flows

Even though I was only SSH’ing into the web server, the web server itself also needed outbound access.

There are actually two separate SSH connections involved.


1. Workstation → Web Server (Remote Trigger)

This is the obvious one:

ssh user@webserver "run-deploy-script.sh"

Firewall requirement:

Allow SSH from the trusted workstation to the DMZ host.


2. Web Server → Git Server (Pull Latest Content)

Inside the deploy script, the DMZ server runs something like:

git fetch origin
git reset --hard origin/main
hugo --minify

That means the DMZ host must also be able to reach the Git server over SSH.

Firewall requirement:

Allow SSH from the DMZ host to the internal Git server.


The Fix: Minimal Rules, Maximum Security

Once I understood the full workflow, the firewall rules became very clean.


Rule A — Trusted Workstation → DMZ Web Server

Allow port 22 only from a trusted source.

Example:

Source: Trusted clients network
Destination: Web server
Port: 22

Rule B — DMZ Web Server → Internal Git Server

Allow outbound SSH only for Git pulls.

Example:

Source: Web server
Destination: Git server
Port: 22

Quick Connectivity Test

A useful test from the DMZ host is:

nc -zv git-server 22

If that fails, your deploy script will not be able to fetch updates.


Final Result

With both rules in place, I ended up with:

  • SSH access locked down to a single trusted machine
  • No unnecessary lateral movement between VLANs
  • A working automated deployment pipeline
  • Only the minimum required ports open

Takeaway

When hardening infrastructure, always map the full flow:

  • Who connects to the server?
  • What does the server need to connect to?

In my case:

Deployment wasn’t just “SSH in” — it was also “Git pull out”.

A small detail, but a very real one.