Skip to content
  • 10 cloud providers
  • 51 configurations
  • Competitive pricing
  • Developer friendly

Firewalls, security groups and network ACLs

Cloud platforms offer several overlapping ways to filter traffic. They behave differently in ways that matter, and using the wrong one produces rules that do not do what you expect.

Abstract illustration accompanying this guide on security groups and network acls

Most cloud platforms give you at least three places to filter network traffic: a firewall on the instance itself, a stateful firewall attached to the instance from outside, and a stateless filter attached to the subnet. They look interchangeable in a diagram and are not.

Getting the distinctions right is the difference between rules that work and rules that appear to work.

Stateful and stateless, which is the key difference

Stateful filtering tracks connections. If you allow an inbound connection on a port, the return traffic is automatically permitted, because the firewall remembers the connection exists. You write one rule and the conversation works in both directions.

Stateless filtering evaluates every packet independently with no memory. Allowing inbound traffic on a port does nothing for the responses; you must explicitly allow the outbound return traffic too, and that return traffic arrives on ephemeral high-numbered ports rather than the port you allowed.

This single difference causes more confusion than anything else in cloud networking. A rule set that works perfectly as a stateful group behaves inexplicably when written as a stateless list, because the responses are silently dropped.

The practical guidance: do your everyday filtering with the stateful mechanism. Use stateless filters only for coarse, subnet-wide rules such as blocking a range of addresses entirely, and be prepared to think carefully about ephemeral ports when you do.

Instance-attached versus subnet-attached

Instance-attached filtering applies to a specific machine or group of machines. Rules follow the workload rather than its location, which means a machine moved between subnets keeps its protections.

Subnet-attached filtering applies to everything in a network segment regardless of what it is. Useful as a coarse boundary, and useful as a backstop, but too blunt for per-service rules.

The usual sensible arrangement is instance-attached rules doing the real work, with subnet-attached rules as an optional additional boundary.

Host firewalls, and why they are not enough alone

A firewall running on the machine, configured with the operating system’s own tooling, is genuinely useful and has one significant weakness: a process that gains sufficient privilege on the machine can change it.

A cloud firewall sits outside the instance and cannot be modified from inside it. That property matters during an incident, and it also means a misconfiguration is fixable from the provider’s console rather than requiring access to a machine you may have just locked yourself out of.

Use both if you like. Rely on the external one.

Reference groups, not addresses

The most useful capability in cloud firewalling, and the most underused, is allowing traffic from another security group rather than from an address range.

Instead of “allow port 5432 from 10.0.2.0/24”, you write “allow port 5432 from the application-servers group”. The rule then stays correct as instances are added, removed and re-addressed. No maintenance, no stale ranges, and it expresses the actual intent: the database accepts connections from application servers, not from a subnet that currently happens to contain them.

Where a platform supports this, use it as the default for internal traffic.

Default deny, and the rule people break

Inbound should default to deny, with explicit allows for exactly what is needed. This is universal advice and almost universally followed.

Outbound is where practice diverges. Default-allow outbound is convenient and normal. Default-deny outbound is meaningfully more secure, because it constrains what a compromised process can reach: data exfiltration and callbacks to command infrastructure both require outbound connectivity.

Restricting outbound traffic is genuinely more work. Package repositories, certificate validation, time synchronisation, and third-party APIs all need allowing, and the failures are confusing when you forget one. It is worth doing for sensitive workloads and often not worth it for everything.

The rule that should never be broken: no database, cache, message broker or administrative interface should be reachable from the public internet. Not temporarily, not behind a strong password. Use a private network or an SSH tunnel.

Writing rules that stay understandable

Comment every rule with why it exists and, ideally, who asked for it. A rule set with no explanations becomes unmodifiable, because nobody dares remove anything.

Group by purpose, not by port. A group for web servers, a group for database access, a group for administrative access. Machines get the groups appropriate to their role.

Avoid one large group applied to everything, because it inevitably becomes the union of every permission anyone ever needed.

Review periodically. Rules accumulate. A twice-yearly pass removing anything nobody can justify keeps the set honest.

Testing that the rules do what you think

Two failures are common and opposite.

Rules that are too permissive and nobody notices, because everything works. Scan your own public addresses from outside and confirm only the intended ports respond. This takes minutes and regularly finds surprises.

Rules that are too restrictive and break something intermittently, because the blocked path is a rarely-used one. Test the unusual paths deliberately: backup jobs, scheduled tasks, health checks from a monitoring service, and administrative access from a different network.

Most cloud platforms also offer a reachability analysis tool that evaluates whether traffic could flow between two points given the current configuration. It is far quicker than reasoning through layered rules by hand, and it is worth knowing exists before you spend an hour puzzling over why a connection times out.

Where you run this matters less than how, but if the provider is still open, the cloud account catalogue compares the options side by side.

For the vendor’s own reference on the services involved here, see the Hetzner documentation.

This is cheap to practise on, which is rather the point of Hetzner. Our Hetzner accounts come in five configurations, and the cloud account catalogue sets out the alternatives.

Questions people ask

What is the difference between stateful and stateless filtering?

Stateful filtering tracks connections, so allowing an inbound connection automatically permits the return traffic. Stateless filtering evaluates each packet independently, so return traffic must be allowed explicitly and arrives on ephemeral high-numbered ports.

Should I use a host firewall or a cloud firewall?

Rely on the cloud firewall, because it sits outside the instance and cannot be modified by a compromised process. A host firewall is a useful additional layer but should not be the only one.

Should I restrict outbound traffic?

It is meaningfully more secure, since exfiltration and callbacks need outbound connectivity, but it is real work: repositories, certificate validation, time sync and third-party APIs all need allowing. Worth doing for sensitive workloads, often not for everything.

What is the best way to write internal rules?

Reference other security groups rather than address ranges. A rule allowing the database to accept connections from the application-server group stays correct as instances come and go, with no maintenance.

Telegram