Your production servers sit behind a firewall. Your deployment tool lives in the cloud. The firewall blocks all incoming connections. So how does your code get from point A to point B?
This is one of the most common deployment challenges teams face — and the solutions range from quick SSH hacks to enterprise VPN infrastructure. Each approach has real trade-offs in security, complexity, and ongoing maintenance.
This guide compares five proven methods for deploying to firewalled servers, with honest assessments of when each one makes sense.
Why Deploying Behind a Firewall Is Hard
The core problem is simple: firewalls block incoming connections by design.
When you deploy code to a publicly accessible server, your deployment tool initiates an SSH or SFTP connection directly to the server. The server accepts the connection, receives the files, and runs any build or restart commands.
But when that server sits behind a corporate firewall, NAT gateway, or private VPC, there is no route in. The deployment tool cannot reach the server because the firewall drops all unsolicited inbound traffic.
flowchart LR
A[DeployTool] -->|Connection attempt| B[Firewall]
B -->|Blocked| C[PrivateServer]
style B fill:#e74c3c,color:#fff
This affects teams in several common scenarios:
- Corporate networks where servers sit behind a strict perimeter firewall
- Cloud VPCs with private subnets and no public IP addresses assigned to instances
- On-premise data centres accessible only via VPN or internal routing
- Regulated environments (healthcare, finance, government) where compliance mandates restrict inbound access
The fundamental question every solution must answer: if the deployment tool cannot connect inbound, how do you get the deployment traffic through?
Every method below solves this in a different way.
Method 1: SSH Reverse Tunnels
An SSH reverse tunnel flips the connection direction. Instead of the deployment tool connecting to the server, the server connects outbound to an intermediary host — and the deployment tool sends traffic back through that tunnel.
flowchart LR
A[PrivateServer] -->|Outbound SSH| B[JumpHost]
C[DeployTool] -->|SSH| B
B -->|Tunnel| A
style B fill:#3498db,color:#fff
How It Works
- You set up a publicly accessible jump host (a small VPS or cloud instance)
- The private server initiates an outbound SSH connection to the jump host, creating a reverse tunnel
- The deployment tool connects to the jump host, which forwards traffic through the tunnel to the private server
Setup Example
On the private server:
ssh -R 2222:localhost:22 user@jumphost.example.com -N
This tells the jump host to forward any connections on port 2222 back through the tunnel to the private server's SSH daemon.
Pros
- Low cost — only requires a small jump host
- No additional software beyond SSH
- Works with any deployment tool that supports SSH
Cons
- Tunnels break. SSH connections drop due to network interruptions, idle timeouts, or server reboots. You need monitoring scripts (autossh or systemd restart policies) to keep them alive
- Requires a publicly accessible jump host — which itself becomes an attack surface
- Only supports SSH and SFTP protocols
- Each server needs its own tunnel — scaling to multiple servers gets messy
- Debugging connection issues through tunnels is painful
When to Use This
One or two servers, temporary or development setups, and situations where you already have a jump host. Not recommended for production deployments that need reliability.
Method 2: VPN-Based Deployment
A VPN extends your deployment tool's network to include the private network, making firewalled servers directly reachable as if they were on the same LAN.
flowchart LR
A[DeployTool] -->|VPN tunnel| B[VPNGateway]
B -->|Internal routing| C[Server1]
B -->|Internal routing| D[Server2]
style B fill:#2ecc71,color:#fff
How It Works
There are two main approaches:
- Site-to-site VPN: Connects two networks permanently. Your deployment infrastructure (or cloud CI/CD service) joins the private network via an IPsec or WireGuard tunnel between gateway devices.
- Client VPN: The deployment tool (or CI runner) connects as a VPN client, receiving a private IP address on the target network.
Once connected, the deployment tool can reach private servers using their internal IP addresses — no port forwarding or tunnels needed.
Pros
- Network-level access — any protocol works (SSH, SFTP, FTP, HTTP)
- Multi-server access through a single VPN connection
- Well-understood technology with mature tooling (WireGuard, OpenVPN, IPsec)
- Strong encryption and authentication
Cons
- Significant infrastructure overhead. You need VPN servers, certificates, key management, and network configuration on both sides
- Higher latency compared to direct connections
- Troubleshooting VPN issues (split tunneling, DNS resolution, routing conflicts) is complex
- Cloud-hosted CI/CD tools (GitHub Actions, GitLab CI) do not natively join arbitrary VPNs — you need self-hosted runners or custom networking
- Ongoing maintenance: VPN software updates, certificate rotation, user management
When to Use This
Organisations that already have VPN infrastructure, or environments where multiple tools (not just deployment) need access to the private network. The cost is justified when the VPN serves broader purposes beyond deployment alone.
Method 3: Bastion Hosts (Jump Servers)
A bastion host is a hardened server placed in a DMZ (demilitarised zone) between the public internet and the private network. It acts as a single, auditable entry point.
flowchart LR
A[DeployTool] -->|SSH| B[BastionHost]
B -->|SSH hop| C[Server1]
B -->|SSH hop| D[Server2]
style B fill:#f39c12,color:#fff
How It Works
- The bastion host has two network interfaces: one facing the internet, one facing the private network
- The deployment tool connects to the bastion via SSH
- The bastion forwards the connection to the target server inside the private network (SSH ProxyJump)
With OpenSSH's ProxyJump directive, this is transparent:
Host private-server
HostName 10.0.1.50
ProxyJump bastion.example.com
User deploy
Pros
- Single entry point simplifies security auditing and access logging
- Well-supported by SSH tooling (
ProxyJump,ProxyCommand) - Multi-server access through one bastion
- Familiar pattern for most sysadmins
Cons
- The bastion itself is an attack surface. It is publicly accessible by design, so it requires hardening, regular patching, and monitoring
- Only supports SSH-based protocols (SFTP, SCP, rsync)
- Adds a network hop — increased latency and a potential single point of failure
- Requires managing SSH keys on the bastion, which can become a security risk if not handled carefully
- Some deployment tools do not support ProxyJump natively
When to Use This
Teams with existing bastion infrastructure, or when compliance requires a single auditable entry point. Common in AWS environments using EC2 Instance Connect or AWS Systems Manager Session Manager as managed bastion alternatives.
Method 4: Self-Hosted CI/CD Runners
Instead of connecting through the firewall, you place a CI/CD runner inside the private network. The runner pulls jobs from the CI/CD service (outbound), executes them locally, and deploys to internal servers directly.
flowchart LR
A[GitHubActions] -->|Job queue| B[Internet]
C[SelfHostedRunner] -->|Polls for jobs| B
C -->|Direct deploy| D[Server1]
C -->|Direct deploy| E[Server2]
style C fill:#9b59b6,color:#fff
How It Works
- You install a runner (GitHub Actions self-hosted runner, GitLab Runner, or similar) on a machine inside the private network
- The runner connects outbound to the CI/CD service and polls for jobs
- When a deployment job is queued, the runner picks it up and executes it locally — with direct network access to all internal servers
Pros
- Runner initiates all connections outbound — no inbound firewall rules needed
- Direct access to all internal servers from the runner
- Full CI/CD pipeline capabilities (build, test, deploy) run inside the private network
- Supported by all major CI/CD platforms (GitHub, GitLab, Jenkins, CircleCI)
Cons
- You are now maintaining CI/CD infrastructure. The runner needs patching, monitoring, and resource management
- Runner has broad network access inside the private network — if compromised, lateral movement is possible
- Build dependencies (Docker, Node, Ruby, etc.) must be installed and maintained on the runner
- Resource contention when multiple jobs run simultaneously
- Not a deployment tool — you are writing deployment scripts inside CI/CD pipelines, which means managing SSH keys, file transfer logic, and rollback procedures yourself
When to Use This
Teams already using GitHub Actions or GitLab CI who need their entire CI/CD pipeline (not just deployment) to run inside the private network. Also useful when deployment is tightly coupled with build and test steps that require access to internal resources.
Method 5: Deployment Agents (Outbound Tunnel)
A deployment agent is a lightweight process that runs inside the private network and establishes a persistent outbound tunnel to the deployment platform. The platform sends deployment traffic through this tunnel — no inbound ports, no VPNs, no bastion hosts.
flowchart LR
A[Agent] -->|Outbound TLS| B[DeployPlatform]
B -->|Deploy traffic| A
A -->|Local network| C[Server1]
A -->|Local network| D[Server2]
style A fill:#1abc9c,color:#fff
How It Works
- You install the agent on any machine inside the private network that has outbound internet access
- The agent establishes a persistent outbound TLS connection to the deployment platform
- When you trigger a deployment, the platform routes traffic through this tunnel
- The agent proxies the traffic to the target server(s) on the local network
This is the approach used by the DeployHQ Agent, Buddy.works Proxy Agent, and Octopus Deploy's Polling Tentacle. For a detailed comparison of how these three agents differ in architecture, protocols, and pricing, see our deployment agents comparison.
The DeployHQ Agent
The DeployHQ Agent connects outbound to agent.deployhq.com on TCP port 7777 via TLS. Once connected, DeployHQ routes all deployment traffic through this tunnel.
Key capabilities:
- Any protocol: FTP, SFTP, SSH, and S3 — no protocol limitations
- Multi-server proxy: A single agent can deploy to multiple internal servers. You control which servers are reachable via an access control file (
~/.deploy/agent.access) using IP addresses, hostnames, or CIDR ranges (e.g.,192.168.1.0/24) - Open source: The agent code is available on GitHub, so you can audit exactly what runs inside your network
- Cross-platform: Runs on Linux, macOS, and Windows
- Minimal requirements: Ruby 2.2+ and a single outbound TCP connection
Setup takes about five minutes:
- Go to Settings > Network Agents in your DeployHQ project
- Click New Network Agent and select your operating system
- Install and start the agent on your server
- Configure the access file with the internal IPs or CIDR ranges you want to reach
- When adding a server in DeployHQ, select Connect via Agent and choose your agent
From that point, deploying to firewalled servers works exactly like deploying to any other server — same interface, same features, same zero-downtime deployment support.
Pros
- Simplest setup — no jump hosts, VPN infrastructure, or CI/CD runners to manage
- All connections are outbound — only one TCP port (7777) needs to be allowed out
- Multi-server access through a single agent with CIDR-based controls
- Protocol-agnostic (FTP, SFTP, SSH, S3)
- Open source and auditable
- Runs as a service — automatic reconnection, no monitoring scripts needed
Cons
- Tied to a specific deployment platform (DeployHQ, Buddy, Octopus)
- Agent must remain running — if it stops, deployments pause until restarted
- Requires Ruby on the agent machine (for DeployHQ's agent specifically)
When to Use This
Teams that want to deploy behind firewalls without managing additional infrastructure. Especially effective when you need to deploy to multiple servers across a private network and want protocol flexibility beyond just SSH.
Comparison: All 5 Methods at a Glance
| SSH Tunnel | VPN | Bastion Host | Self-Hosted Runner | Deployment Agent | |
|---|---|---|---|---|---|
| Setup complexity | Medium | High | Medium-High | Medium | Low |
| Ongoing maintenance | High | High | Medium | Medium | Low |
| Inbound ports required | Yes (jump host) | No | Yes (bastion) | No | No |
| Protocol support | SSH/SFTP only | Any | SSH only | Depends on scripts | Any |
| Multi-server | Manual per-server | Yes | Yes (SSH hops) | Yes | Yes (CIDR-based) |
| Additional infrastructure | Jump host | VPN server | Bastion instance | Runner instance | None |
| Security model | Point-to-point | Network-level | Single entry point | Runner in network | Outbound TLS only |
| Reliability | Low (tunnels break) | High | Medium | Medium | High (auto-reconnect) |
| Cost | Low | Medium-High | Medium | Free-Medium | Included with platform |
Choosing the Right Approach
There is no single best method. The right choice depends on your existing infrastructure and what you are trying to accomplish.
flowchart TD
A[Need to deploy behind firewall?] -->|Yes| B{Already have VPN?}
B -->|Yes| C[Use VPN]
B -->|No| D{Need full CI/CD inside network?}
D -->|Yes| E[Self-Hosted Runner]
D -->|No| F{Multiple servers?}
F -->|Yes| G[Deployment Agent]
F -->|No| H{Temporary setup?}
H -->|Yes| I[SSH Reverse Tunnel]
H -->|No| G
By Scenario
Single server behind a corporate firewall: Start with a deployment agent. It is the fastest path to working deployments with no infrastructure to build. If the agent approach does not fit your constraints, a bastion host is the next best option.
Multiple servers in a private network: A deployment agent with CIDR-based access or a VPN. Avoid SSH tunnels — managing individual tunnels per server does not scale.
Cloud VPC without public IPs: If you are on AWS, consider AWS Systems Manager Session Manager for ad-hoc access, combined with a deployment agent for automated deployments. On other clouds, a deployment agent or self-hosted runner are the most practical options.
Compliance-restricted environments (healthcare, finance): A bastion host provides the audit trail that compliance frameworks typically require. Pair it with a deployment agent for the actual deployment traffic — the bastion handles human access, the agent handles automated deployments.
Security Considerations
Each method exposes a different attack surface. Here is what to watch for:
SSH tunnels create a persistent connection through the firewall. If the jump host is compromised, the attacker has a direct path into your private network. Harden the jump host, restrict SSH access by IP, and use key-based authentication only.
VPNs extend network boundaries. A compromised VPN client can access anything on the private network. Use split tunneling to limit access, and implement network segmentation so the VPN subnet can only reach deployment targets.
Bastion hosts are publicly accessible by design. They must be hardened aggressively: minimal packages, fail2ban, key-only authentication, session logging, and regular patching. Many teams are now replacing traditional bastions with zero-trust alternatives.
Self-hosted runners have broad internal network access. If a malicious pull request triggers a workflow, the runner executes it inside your network. Restrict runner permissions, avoid running untrusted code, and isolate runners in their own network segment.
Deployment agents have the smallest attack surface: a single outbound TLS connection with no listening ports. The agent only proxies traffic to explicitly whitelisted servers (via the access control file). The main risk is the agent machine itself — keep it patched and restrict who can modify the access list.
Getting Started
If you are deploying behind a firewall with DeployHQ, the Agent is the recommended approach. It requires no additional infrastructure, supports all deployment protocols, and takes about five minutes to set up.
- Read the Agent setup documentation
- Install the agent on a machine inside your private network
- Configure the access file with your internal server IPs
- Start deploying — same workflow as any other server
For more deployment security practices, see our security checklist for deployments and practical security tips for smarter deployments.
Need help setting up deployments to your firewalled servers? Reach out to us at support@deployhq.com or on Twitter/X. If you are not using DeployHQ yet, start a free trial — the Agent is available on all plans.