How to create a jumpbox to securely connect to your compute instances running on Civo?

Learn how to secure Civo Compute instances by placing them on a private network and accessing them through a single jumpbox using firewall rules and SSH ProxyJump.

6 minutes reading time

Written by

Vikas Yadav
Vikas Yadav

Founder at KubeNine

Stop exposing every Civo instance: put them behind a private network and one jumpbox

When you spin up a Compute instance on Civo, it gets a public IPv4 address by default. That's convenient for a quick test box. It's a liability for a fleet. Every instance with a public IP is an instance with an internet-facing SSH port, and "we'll lock the firewall down later" is how port 22 ends up in everyone's logs.

There's a cleaner pattern, and Civo gives you everything you need for it out of the box: drop your workload instances onto a private network with no public IP at all, and reach them through a single jumpbox (bastion host) that is the only machine exposed to the internet. Humans SSH into the jumpbox; the jumpbox is the only door to everything else.

If you've built this on AWS, you're bracing for the annoying part: a NAT gateway so your private instances can still run apt update. On Civo you can relax. Instances on a private network get outbound internet access automatically — there's no NAT gateway to provision, configure, or pay for. Your private boxes can pull packages on day one while staying completely unreachable from the outside.

Here's the whole thing, built with the Civo CLI.

Architecture overview

How to create jumpbox to securely connect to your compute instances running on civo?

Three pieces:

  • A private network that all the instances share and route over internally.
  • Backend instances with --publicip=none. They have no internet-facing surface. They still get outbound internet automatically.
  • One jumpbox with a public IP, sitting on the same private network, locked down so only you can SSH in.

Two firewalls enforce the boundary: one that exposes only the jumpbox's SSH port to your IP, and one that lets the backends accept SSH only from inside the private network.

Prerequisites

You need the Civo CLI installed and authenticated:

civo apikey show # Confirm you're pointed at the correct account
civo sshkey ls # List your uploaded SSH keys

If you haven't uploaded an SSH key yet:

civo sshkey create my-key --key ~/.ssh/id_ed25519.pub

Everything below assumes a single region (set yours with civo region current <code> if needed).

Step 1: Create the private network

civo network create secure-net

Confirm it and note its CIDR — you'll need it for the backend firewall rule:

civo network ls
Step 1: Create the private network

I'll use 192.168.1.0/24 from here on — substitute whatever Civo assigned you.

Step 2: Create the two firewalls

This is where most "secure" tutorials quietly betray you. By default, civo firewall create ships the firewall with default open rules. We don't want that. The --no-default-rules flag gives you a clean slate so the only rules on each firewall are the ones you add deliberately.

Jumpbox firewall — the only thing exposed to the internet:

civo firewall create jumpbox-fw --network secure-net --no-default-rules

Allow SSH inbound, and only from your own IP. Find yours with curl -s ifconfig.me, then:

civo firewall rule create jumpbox-fw \
--protocol TCP --startport 22 --endport 22 \
--cidr 203.0.113.10/32 \
--direction ingress --action allow \
--label "SSH from my workstation"

Replace 203.0.113.10/32 with your address. This single /32 rule is the difference between a bastion and a billboard.

Backend firewall — no public door at all, SSH reachable only from inside the private network:

civo firewall create backend-fw --network secure-net --no-default-rules
civo firewall rule create backend-fw \
--protocol TCP --startport 22 --endport 22 \
--cidr 192.168.1.0/24 \
--direction ingress --action allow \
--label "SSH from private network only"

Since the backends won't have a public IP, this rule is really belt-and-suspenders, but it's the kind you want. The only host that lives on 192.168.1.0/24 and also faces the internet is the jumpbox, so in practice SSH to a backend can only come from the jumpbox.

Step 3: Launch the backend instances

The headline flag is --publicip=none. The CLI defaults this to create, which is exactly the public-by-default behavior we're getting away from.

civo instance create backend-01 \
--size g3.small \
--diskimage ubuntu-noble \
--publicip none \
--network secure-net \
--firewall backend-fw \
--sshkey my-key \
--initialuser civo \
--wait
civo instance create backend-02 \
--size g3.small \
--diskimage ubuntu-noble \
--publicip none \
--network secure-net \
--firewall backend-fw \
--sshkey my-key \
--initialuser civo \
--wait

(Use civo size ls and civo diskimage ls to pick a size and image that suit you.)

These instances now have no public IP. From the outside, they don't exist. From the inside, log into one later and run apt update — it just works, because Civo provisions outbound access for private instances automatically. No NAT gateway, no route tables, no extra cost.

Grab their private IPs:

civo instance show backend-01 -o custom -f "Hostname,PrivateIP"
civo instance show backend-02 -o custom -f "Hostname,PrivateIP"

Say they come back as 192.168.1.3 and 192.168.1.4.

Step 4: Launch the jumpbox

Same network, but with a public IP and the locked-down jumpbox firewall:

civo instance create jumpbox \
--size g3.small \
--diskimage ubuntu-noble \
--publicip create \
--network secure-net \
--firewall jumpbox-fw \
--sshkey my-key \
--initialuser civo \
--wait

Get its public IP:

civo instance show jumpbox -o custom -f "Hostname,PublicIP,PrivateIP"

Say it's 74.220.20.5 (public) and 192.168.1.2 (private).

Step 5: Connect through the jumpbox with ProxyJump

The right way to hop through a bastion is SSH's ProxyJump. Your private key stays on your laptop and is never copied to the jumpbox — the jumpbox just forwards the encrypted connection. (Resist the temptation to scp your private key onto the bastion "to make it easier." A compromised bastion with your key on it owns your whole fleet.)

Add this to ~/.ssh/config:

Host civo-jump
HostName 74.220.20.5
User civo
Host backend-01
HostName 192.168.1.3
User civo
ProxyJump civo-jump
Host backend-02
HostName 192.168.1.4
User civo
ProxyJump civo-jump

Now reach a private backend directly:

ssh backend-01

SSH transparently lands on the jumpbox first, then tunnels to 192.168.1.3 over the private network. One command, no key on the bastion, and backend-01 was never reachable from the internet to begin with.

A one-off without editing your config looks like this:

ssh -J civo@74.220.20.5 civo@192.168.1.3

Verify the boundary actually holds

The point of all this is what doesn't work. Confirm it:

# Backend instances have no public IPs.
# Attempting to SSH directly should fail because there is no public IP.
ssh civo@<backend-public-ip>
# SSH to the jumpbox from an unauthorized IP should be blocked by the firewall.
# Backend instances should be reachable only through the jumpbox.
ssh backend-01 'hostname && curl -s ifconfig.me'

That last command is worth pausing on: it pulls outbound internet from a box with no public IP. On most clouds you'd have had to stand up a NAT gateway first.

Tightening from here (optional)

This is a deliberately minimal build. A few directions to harden it when you're ready:

  • Keep the jumpbox firewall rule scoped to a `/32`. If your IP is dynamic, scope it to your office/VPN CIDR rather than 0.0.0.0/0.
  • For team access, you don't have to hand out the jumpbox to everyone manually — front it with an SSO/identity-aware access layer (Teleport, Tailscale SSH, Cloudflare Access, and friends) so logins are tied to your identity provider and audited centrally.
  • Disable password auth and root login on the jumpbox.

Tear it down

civo instance rm jumpbox backend-01 backend-02 --yes
civo firewall rm jumpbox-fw backend-fw --yes
civo network rm secure-net --yes

FAQs

Summary

A public IP for every compute instance may be convenient during development, but it isn't the best approach for production environments. By combining a private network, two firewalls, and a single jumpbox, you can keep your Civo Compute instances hidden from the public internet while still allowing outbound internet access for updates and package installations—without deploying a NAT Gateway. This approach reduces your attack surface, centralizes SSH access, and provides a simpler, more secure way to manage your infrastructure on Civo.

Read more

Vikas Yadav
Vikas Yadav

Founder at KubeNine

Vikas Yadav is the founder of Kubenine and a DevOps engineer focused on building scalable cloud infrastructure and developer platforms. His work centers on automation, platform engineering, and modern DevOps practices using technologies such as AWS, Kubernetes, Terraform, and CI/CD pipelines.

With more than seven years of experience in cloud engineering, including previous work at LinkedIn, Vikas helps startups and enterprise teams improve deployment workflows, automate infrastructure, and optimize cloud environments.

View author profile