Firewall
Section titled “Firewall”This page covers packet-level control of container traffic: what can connect in (ingress) and what the container can reach out to (egress).
API endpoints summary
Section titled “API endpoints summary”Complete endpoint documentation:
- GET /api/v1/containers/{id}/firewall/rules - List all firewall rules
- POST /api/v1/containers/{id}/firewall/ingress - Add ingress rule
- POST /api/v1/containers/{id}/firewall/egress - Add egress rule
- DELETE /api/v1/containers/{id}/firewall/ingress - Remove ingress rule(s)
- DELETE /api/v1/containers/{id}/firewall/egress - Remove egress rule(s)
- PATCH /api/v1/containers/{id}/firewall/ingress - Toggle ingress rule state
- PATCH /api/v1/containers/{id}/firewall/egress - Toggle egress rule state
- POST /api/v1/containers/{id}/firewall/reset - Reset firewall
Enforcement, directions, and actions
Section titled “Enforcement, directions, and actions”The firewall runs on your server’s host kernel, completely outside the containers.
What that means:
- Containers cannot bypass firewall rules
- Containers cannot modify their own firewall
- Rules survive container restarts and snapshots
Two directions:
- Ingress - traffic coming into the container (who can connect)
- Egress - traffic going out from the container (what the container can reach)
Three actions:
- allow - permit matching traffic
- reject - block and notify (ICMP/TCP unreachable)
- drop - silently ignore (appears offline to scanners)
Three protocols: tcp, udp, icmp4
Rule limits
Section titled “Rule limits”A container’s rule set is bounded two ways, and both are per container rather than per direction:
- Count: 500 rules by default, with ingress and egress sharing that budget.
- Size: 262,144 bytes of serialized rules by default. A long rule is roughly 2 KB, so a rule set of verbose rules can reach the byte budget before it reaches 500 rules.
Whichever binds first stops the write. An add over the count returns 400 CONTAINER_FIREWALL_RULE_LIMIT, and one over the size returns 400 CONTAINER_FIREWALL_BYTES_LIMIT; both messages carry the current value and the limit. Remove or shorten rules rather than retrying. Both defaults are operator-configurable, so a self-hosted deployment may be set higher or lower.
Examples
Section titled “Examples”Allow SSH from a specific IP
Section titled “Allow SSH from a specific IP”# Add an ingress rulehoody firewall ingress create -c $CONTAINER_ID --action allow --protocol tcp --description "Allow SSH from office" --destination-port 22 --source 203.0.113.50/32await client.api.firewall.createIngressRule(CONTAINER_ID, { action: 'allow', protocol: 'tcp', description: 'Allow SSH from office', destination_port: '22', source: '203.0.113.50/32',});curl -X POST "https://api.hoody.com/api/v1/containers/$CONTAINER_ID/firewall/ingress" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "action": "allow", "protocol": "tcp", "description": "Allow SSH from office", "destination_port": "22", "source": "203.0.113.50/32" }'One request, one link
cURL runs inside your container and can wrap any HTTP request into a single GET URL. The call stops being something you need a client for and becomes something you can paste into a browser, send in a chat, bookmark, schedule with cron, or drop into a no-code tool.
Nothing is installed on the machine that opens it. The link does carry whatever credentials the call needs, so treat it as you would treat those credentials.
Slashes, colons and braces pass through as they are. The one character you must
encode is an & inside a value, which happens when the wrapped URL
carries its own query string. Left raw it ends the value early, and the rest is
read as cURL's own parameters, so you get a 200 on a request you did
not make.
How the wrapping works Chaining calls into one link Turning a link into a shortcut
Adds the ingress rule as a single GET link, ready to paste into a browser.
https://PROJECT_ID-CONTAINER_ID-curl-1.SERVER.containers.hoody.com/api/v1/curl/request?url=https://api.hoody.com/api/v1/containers/CONTAINER_ID/firewall/ingress&method=POST&bearer_token=TOKEN&json={"action":"allow","protocol":"tcp","description":"Allow%20SSH%20from%20office","destination_port":"22","source":"203.0.113.50/32"}&response=transparent The link carries a credential and executes with it, so it is as sensitive as the credential itself — and it passes through the cURL service's request log on the way, not just the target's. Share it only where you would share the secret, and prefer a delegated token with minimal permissions and an expiry: see API tokens.
Block all outbound TCP traffic
Section titled “Block all outbound TCP traffic”# Add an egress rulehoody firewall egress create -c $CONTAINER_ID --action drop --protocol tcp --description "Block all TCP egress" --destination 0.0.0.0/0 --destination-port 1-65535await client.api.firewall.createEgressRule(CONTAINER_ID, { action: 'drop', protocol: 'tcp', description: 'Block all TCP egress', destination: '0.0.0.0/0', destination_port: '1-65535',});curl -X POST "https://api.hoody.com/api/v1/containers/$CONTAINER_ID/firewall/egress" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "action": "drop", "protocol": "tcp", "description": "Block all TCP egress", "destination": "0.0.0.0/0", "destination_port": "1-65535" }'One request, one link
cURL runs inside your container and can wrap any HTTP request into a single GET URL. The call stops being something you need a client for and becomes something you can paste into a browser, send in a chat, bookmark, schedule with cron, or drop into a no-code tool.
Nothing is installed on the machine that opens it. The link does carry whatever credentials the call needs, so treat it as you would treat those credentials.
Slashes, colons and braces pass through as they are. The one character you must
encode is an & inside a value, which happens when the wrapped URL
carries its own query string. Left raw it ends the value early, and the rest is
read as cURL's own parameters, so you get a 200 on a request you did
not make.
How the wrapping works Chaining calls into one link Turning a link into a shortcut
Adds the egress-drop rule as a single GET link. Route it through a different running container’s curl-1 (OTHER_CONTAINER_ID below): this rule cuts CONTAINER_ID’s own outbound TCP, including the request applying it. This blocks new TCP connections only; pair it with the UDP and ICMP versions below. Host DNS and established connections stay accepted either way. Container networking is IPv4-only, so there is no IPv6 path these rules miss.
https://PROJECT_ID-OTHER_CONTAINER_ID-curl-1.SERVER.containers.hoody.com/api/v1/curl/request?url=https://api.hoody.com/api/v1/containers/CONTAINER_ID/firewall/egress&method=POST&bearer_token=TOKEN&json={"action":"drop","protocol":"tcp","description":"Block%20all%20TCP%20egress","destination":"0.0.0.0/0","destination_port":"1-65535"}&response=transparent The link carries a credential and executes with it, so it is as sensitive as the credential itself — and it passes through the cURL service's request log on the way, not just the target's. Share it only where you would share the secret, and prefer a delegated token with minimal permissions and an expiry: see API tokens.
Use case: closing a container’s outbound TCP. Traffic on other protocols needs its own rules, DNS to the container’s host is accepted ahead of these rules, and established connections keep flowing.
Allow one service only
Section titled “Allow one service only”Allow HTTPS to a specific API. On its own this rule changes nothing, because unmatched traffic is already allowed; it takes effect once the drops below leave a hole for it:
# Add an egress rulehoody firewall egress create -c $CONTAINER_ID --action allow --protocol tcp --description "Allow HTTPS to API" --destination-port 443 --destination 198.51.100.0/24await client.api.firewall.createEgressRule(CONTAINER_ID, { action: 'allow', protocol: 'tcp', description: 'Allow HTTPS to API', destination_port: '443', destination: '198.51.100.0/24',});curl -X POST "https://api.hoody.com/api/v1/containers/$CONTAINER_ID/firewall/egress" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "action": "allow", "protocol": "tcp", "description": "Allow HTTPS to API", "destination_port": "443", "destination": "198.51.100.0/24" }'One request, one link
cURL runs inside your container and can wrap any HTTP request into a single GET URL. The call stops being something you need a client for and becomes something you can paste into a browser, send in a chat, bookmark, schedule with cron, or drop into a no-code tool.
Nothing is installed on the machine that opens it. The link does carry whatever credentials the call needs, so treat it as you would treat those credentials.
Slashes, colons and braces pass through as they are. The one character you must
encode is an & inside a value, which happens when the wrapped URL
carries its own query string. Left raw it ends the value early, and the rest is
read as cURL's own parameters, so you get a 200 on a request you did
not make.
How the wrapping works Chaining calls into one link Turning a link into a shortcut
Adds the egress-allow rule as a single GET link, scoped to HTTPS traffic toward the given CIDR.
https://PROJECT_ID-CONTAINER_ID-curl-1.SERVER.containers.hoody.com/api/v1/curl/request?url=https://api.hoody.com/api/v1/containers/CONTAINER_ID/firewall/egress&method=POST&bearer_token=TOKEN&json={"action":"allow","protocol":"tcp","description":"Allow%20HTTPS%20to%20API","destination_port":"443","destination":"198.51.100.0/24"}&response=transparent The link carries a credential and executes with it, so it is as sensitive as the credential itself — and it passes through the cURL service's request log on the way, not just the target's. Share it only where you would share the secret, and prefer a delegated token with minimal permissions and an expiry: see API tokens.
Port specification
Section titled “Port specification”{"destination_port": "80"}{"destination_port": "8000-9000"}{"destination_port": "80,443,8080"}{"destination_port": "22,80-90,443,8000-9000"}CIDR notation:
/32- a single IP (203.0.113.50/32)/24- 256 IPs (203.0.113.0/24)/0- all IPs (0.0.0.0/0)
Rule evaluation order
Section titled “Rule evaluation order”Order is fixed by action, not by the order you create rules in: every drop rule is evaluated first, then every reject rule, then every allow rule. Traffic that matches nothing is allowed, because the bridge default stays open.
Two consequences decide how you write rules:
- An allow rule cannot carve an exception out of an overlapping drop. Allowing SSH from your office and then dropping port 22 from
0.0.0.0/0drops your office too. - An allow rule on its own restricts nothing, since unmatched traffic was already allowed. It only documents intent, or re-permits traffic a narrower drop does not cover.
So an allowlist has to be written as drops that exclude what you keep. Ports make that easy: to leave only 443 open, drop 1-442 and 444-65535 rather than 1-65535. Sources do not: the complement of one network is many ranges, so where you need “only this source”, use a mechanism with allowlist semantics instead, such as a program sandbox’s ingress_allow_from for one daemon program, or proxy permissions for an HTTP service.
The pair below shows the trap. The allow is created first, but the drop still wins:
Creating them in the other order changes nothing: the drop is evaluated first either way, so SSH from the office is dropped as well. To leave SSH reachable from one network only, drop the complement of that network as shown under restrict a port to one source network. The SSH gateway is the other route in, but it authenticates by public key rather than by source address, so it is a different control, not a source allowlist.
Common patterns
Section titled “Common patterns”Restrict access to a database container
Section titled “Restrict access to a database container”Close PostgreSQL at the firewall. The allow records the source you mean to keep; the drop that follows is evaluated first and closes the port for that source too:
Drop attempts on that port. This drop is evaluated before the allow above, so it closes the port for everyone, the allowed source included; keep it only if you want the port closed at the firewall and reached another way:
Serve the web publicly and restrict SSH
Section titled “Serve the web publicly and restrict SSH”Allow HTTP/HTTPS from anywhere:
Close SSH at the firewall. As above, the drop is evaluated before the allow, so the office range is closed as well; reach SSH through the SSH gateway instead:
Drop SSH. Like every drop, it is evaluated before the allow above and therefore also closes the office range; reach SSH through the SSH gateway instead of opening it here:
Close a container’s outbound paths
Section titled “Close a container’s outbound paths”Block all egress:
Rules match per protocol, so all three (tcp, udp, icmp4) are required. Otherwise ICMP egress stays open under the default-allow policy.
Use this for agent runs you want kept off the network until you say otherwise. “Off the network” here means new IPv4 connections that match these rules: DNS to the container’s host is accepted ahead of them, established and related traffic keeps flowing. Container networking is IPv4-only, so there is no IPv6 path these rules miss. An allowed destination is itself a way out, so egress rules are not proof that data stayed on the machine.
Leave only the ports you need open
Section titled “Leave only the ports you need open”Both ports and destinations can be restricted, but only by dropping the complement of what you keep. Ports make that short; destinations mean enumerating the CIDR ranges that surround the addresses you allow.
Rule order is fixed and does not follow creation order: drop rules are evaluated first, then reject rules, then allow rules. An allow rule therefore cannot carve an exception out of an overlapping drop, so a deny range must exclude the ports or destinations you allow.
Allow only what your app needs (e.g., your API):
Allow DNS:
Block everything else. Firewall rules are per-protocol, so a drop is needed for each of tcp, udp and icmp4, and each drop must leave out the ports you allowed above: dropping 1-65535 would close 443 too, because drops are evaluated before allows.
What is left open is new TCP to port 443 and UDP to port 53, at any destination, plus host DNS and established connections. These rules restrict ports: the allow rules above record which destination you had in mind, but a drop cannot be narrowed by them. To restrict destinations as well, the drops have to enumerate the CIDR ranges around the addresses you allow. Where traffic must be limited by destination, send it through a proxy you control.
Firewall vs Hoody Proxy
Section titled “Firewall vs Hoody Proxy”The two are separate security layers:
| Layer | Controls | Use For |
|---|---|---|
| Firewall | Network packets (TCP/UDP/ICMP) | Closing ports, dropping sources you name |
| Proxy Permissions | HTTP service access | User auth, service-level control, allowing client IPs you name |
The firewall controls network traffic; Proxy Permissions controls HTTP service access. Use both together to cover packets and HTTP requests. They are not disjoint paths: the proxy’s upstream connection to the container is itself traffic these rules can block.
A program sandbox is a separate layer inside the container, independent of the rules on this page. An optional program sandbox restricts one daemon program. Under network.mode: restricted Landlock limits the TCP ports it may bind and connect to, for every destination including loopback and the platform address, but not UDP or destination hosts, and refuses MPTCP, io_uring and TCP Fast Open sends so those limits cannot be bypassed; mode: none removes its external networking, UDP included, while Unix sockets reachable by path stay reachable. Setting network.udp: "deny" stops the program from creating UDP sockets under any mode. Separately, its ingress rules filter IPv4 TCP arriving on eth0 at its declared ports. By default those rules let packets whose source is loopback or the gateway through; a public request forwarded by the proxy keeps the client’s own address and is filtered.
Allow only certain client IPs
Section titled “Allow only certain client IPs”Three layers can restrict who reaches a service, and which one to use depends on what is listening:
- An HTTP service behind a container URL: use proxy permissions. An
ipgroup matches one IPv4 CIDR, with/32for a single address. Membership is not by itself a restriction: grant that group access to the service, make the effective defaultdeny, and check that no other group admits callers from elsewhere. The effective default is the container document’s when it sets one and the project document’s otherwise, so a project set todenyis undone by a container left atallow. This is the layer to reach for, because it sees the HTTP request rather than a packet, and because it fails closed once the default isdeny. - One daemon program’s own listening port: use the program sandbox. A non-empty
ingress_allow_fromtakes up to 64 IPv4 CIDRs and drops other new connections arriving oneth0at that program’s declared ports. An empty or omitted list restricts nothing. It needs a declared listening set, covers IPv4 TCP only, and lets loopback and the gateway through unless you setingress_allow_platform: false. A public request forwarded by the proxy keeps the client’s own address, so it is filtered here. - The whole container, on IPv4 TCP, UDP or ICMPv4: use the rules on this page, written as drops. They have no allowlist semantics, so you drop every source range except the ones you keep, once per protocol. One rule can carry several comma-separated CIDRs, so excluding a single
/24stays manageable; excluding scattered addresses rarely does.
Both firewall layers exempt established and related connections, so a rule change reaches new connections only. Editing a sandbox policy is the exception: it reloads the program’s definition and can stop its listener, which closes what was open.
See: Proxy Permissions →
Manage rules
Section titled “Manage rules”List rules
Section titled “List rules”# List all firewall rules for a containerhoody firewall rules list -c $CONTAINER_IDconst { data } = await client.api.firewall.listRules(CONTAINER_ID);console.log(data);curl "https://api.hoody.com/api/v1/containers/$CONTAINER_ID/firewall/rules" \ -H "Authorization: Bearer $TOKEN"One request, one link
cURL runs inside your container and can wrap any HTTP request into a single GET URL. The call stops being something you need a client for and becomes something you can paste into a browser, send in a chat, bookmark, schedule with cron, or drop into a no-code tool.
Nothing is installed on the machine that opens it. The link does carry whatever credentials the call needs, so treat it as you would treat those credentials.
Slashes, colons and braces pass through as they are. The one character you must
encode is an & inside a value, which happens when the wrapped URL
carries its own query string. Left raw it ends the value early, and the rest is
read as cURL's own parameters, so you get a 200 on a request you did
not make.
How the wrapping works Chaining calls into one link Turning a link into a shortcut
Lists the container’s firewall rules as a single GET link.
https://PROJECT_ID-CONTAINER_ID-curl-1.SERVER.containers.hoody.com/api/v1/curl/request?url=https://api.hoody.com/api/v1/containers/CONTAINER_ID/firewall/rules&method=GET&bearer_token=TOKEN&response=transparent The link carries a credential and executes with it, so it is as sensitive as the credential itself — and it passes through the cURL service's request log on the way, not just the target's. Share it only where you would share the secret, and prefer a delegated token with minimal permissions and an expiry: see API tokens.
Disable a rule without removing it
Section titled “Disable a rule without removing it”# Disable an ingress rulehoody firewall ingress disable -c $CONTAINER_ID --description "Allow SSH from office"await client.api.firewall.disableIngressRule(CONTAINER_ID, { description: 'Allow SSH from office',});curl -X PATCH "https://api.hoody.com/api/v1/containers/$CONTAINER_ID/firewall/ingress" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{"state": "disabled", "description": "Allow SSH from office"}'One request, one link
cURL runs inside your container and can wrap any HTTP request into a single GET URL. The call stops being something you need a client for and becomes something you can paste into a browser, send in a chat, bookmark, schedule with cron, or drop into a no-code tool.
Nothing is installed on the machine that opens it. The link does carry whatever credentials the call needs, so treat it as you would treat those credentials.
Slashes, colons and braces pass through as they are. The one character you must
encode is an & inside a value, which happens when the wrapped URL
carries its own query string. Left raw it ends the value early, and the rest is
read as cURL's own parameters, so you get a 200 on a request you did
not make.
How the wrapping works Chaining calls into one link Turning a link into a shortcut
Toggles the matching ingress rule to disabled as a single GET link, without deleting it.
https://PROJECT_ID-CONTAINER_ID-curl-1.SERVER.containers.hoody.com/api/v1/curl/request?url=https://api.hoody.com/api/v1/containers/CONTAINER_ID/firewall/ingress&method=PATCH&bearer_token=TOKEN&json={"state":"disabled","description":"Allow%20SSH%20from%20office"}&response=transparent The link carries a credential and executes with it, so it is as sensitive as the credential itself — and it passes through the cURL service's request log on the way, not just the target's. Share it only where you would share the secret, and prefer a delegated token with minimal permissions and an expiry: see API tokens.
Remove rules
Section titled “Remove rules”Remove a specific rule:
# Remove a specific ingress rulehoody firewall ingress delete -c $CONTAINER_ID --description "Allow SSH from office" --yesawait client.api.firewall.deleteIngressRule(CONTAINER_ID, { description: 'Allow SSH from office',});curl -X DELETE "https://api.hoody.com/api/v1/containers/$CONTAINER_ID/firewall/ingress" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{"description": "Allow SSH from office"}'One request, one link
cURL runs inside your container and can wrap any HTTP request into a single GET URL. The call stops being something you need a client for and becomes something you can paste into a browser, send in a chat, bookmark, schedule with cron, or drop into a no-code tool.
Nothing is installed on the machine that opens it. The link does carry whatever credentials the call needs, so treat it as you would treat those credentials.
Slashes, colons and braces pass through as they are. The one character you must
encode is an & inside a value, which happens when the wrapped URL
carries its own query string. Left raw it ends the value early, and the rest is
read as cURL's own parameters, so you get a 200 on a request you did
not make.
How the wrapping works Chaining calls into one link Turning a link into a shortcut
Removes the matching ingress rule as a single GET link.
https://PROJECT_ID-CONTAINER_ID-curl-1.SERVER.containers.hoody.com/api/v1/curl/request?url=https://api.hoody.com/api/v1/containers/CONTAINER_ID/firewall/ingress&method=DELETE&bearer_token=TOKEN&json={"description":"Allow%20SSH%20from%20office"}&response=transparent The link carries a credential and executes with it, so it is as sensitive as the credential itself — and it passes through the cURL service's request log on the way, not just the target's. Share it only where you would share the secret, and prefer a delegated token with minimal permissions and an expiry: see API tokens.
Reset completely, removing all rules:
# Reset firewall to default (removes ALL rules)hoody firewall reset -c $CONTAINER_ID --yesawait client.api.firewall.reset(CONTAINER_ID);curl -X POST "https://api.hoody.com/api/v1/containers/$CONTAINER_ID/firewall/reset" \ -H "Authorization: Bearer $TOKEN"One request, one link
cURL runs inside your container and can wrap any HTTP request into a single GET URL. The call stops being something you need a client for and becomes something you can paste into a browser, send in a chat, bookmark, schedule with cron, or drop into a no-code tool.
Nothing is installed on the machine that opens it. The link does carry whatever credentials the call needs, so treat it as you would treat those credentials.
Slashes, colons and braces pass through as they are. The one character you must
encode is an & inside a value, which happens when the wrapped URL
carries its own query string. Left raw it ends the value early, and the rest is
read as cURL's own parameters, so you get a 200 on a request you did
not make.
How the wrapping works Chaining calls into one link Turning a link into a shortcut
Removes every rule and resets the firewall to default as a single GET link.
https://PROJECT_ID-CONTAINER_ID-curl-1.SERVER.containers.hoody.com/api/v1/curl/request?url=https://api.hoody.com/api/v1/containers/CONTAINER_ID/firewall/reset&method=POST&bearer_token=TOKEN&response=transparent The link carries a credential and executes with it, so it is as sensitive as the credential itself — and it passes through the cURL service's request log on the way, not just the target's. Share it only where you would share the secret, and prefer a delegated token with minimal permissions and an expiry: see API tokens.
Best practices
Section titled “Best practices”Restrict a port to one source network
Section titled “Restrict a port to one source network”Creation order does not help here, because rules are evaluated by action: an allow can never carve an exception out of a drop that covers the same traffic. To leave a port open to one network only, drop that network’s complement, which is every range except the one you keep. A single rule carries the whole list, since source accepts comma-separated CIDRs. Separate them with commas and no spaces: the API rejects anything outside digits, dots, slashes and commas with a 400.
The complement of a /8 is eight ranges. This leaves SSH reachable from 10.0.0.0/8 and closed to everyone else:
The shorter the prefix you keep, the shorter the complement: a /8 needs eight ranges, a /24 needs twenty-four. Add one rule per protocol you want closed, since a TCP rule leaves UDP and ICMPv4 alone.
A rule that names addresses is IPv4 only, because the API rejects IPv6 addresses and ICMPv6 outright. Container networking is IPv4-only, so that is the whole of it: there is no second family for a rule to miss.
Use drop for public services
Section titled “Use drop for public services”"action": "drop"= stealth (appears offline to scanners)"action": "reject"= reveals existence (sends ICMP unreachable)
Allow DNS when restricting egress
Section titled “Allow DNS when restricting egress”When blocking egress, remember to allow DNS:
Snapshot before major changes
Section titled “Snapshot before major changes” See: Snapshots →
Useful questions
Section titled “Useful questions”Does the firewall apply to traffic through the Hoody Proxy?
Section titled “Does the firewall apply to traffic through the Hoody Proxy?”Partly. The proxy terminates the request and opens its own connection to the container, so these rules never see the original HTTP request. That upstream connection does traverse them: it leaves the host through the container’s bridge, where the same ingress rules are evaluated. For an ordinary public client it carries that client’s own address as its source; for a hairpinned request, one sent from a container on the same server, it carries the address the client dialed instead. A rule matching the service’s port or the client’s network therefore blocks a service URL as well. Use Proxy Permissions for HTTP service access control, and treat these rules as packet-level policy that applies to both paths.
Can containers modify their own firewall rules?
Section titled “Can containers modify their own firewall rules?”No. Firewall rules are host-enforced and only modifiable via the Hoody API. Containers cannot see or change them.
How does block mode differ from egress rules?
Section titled “How does block mode differ from egress rules?”Network Configuration block mode blackholes all outbound TCP from the container, including calls to other containers’ service URLs. Firewall egress rules are granular: allow some destinations, block others.
Do firewall rules persist through restarts?
Section titled “Do firewall rules persist through restarts?”Yes. Rules are stored at host level and survive container restarts, pauses, and snapshots.
What if I lock myself out with firewall rules?
Section titled “What if I lock myself out with firewall rules?”Open the hoody-terminal URL. That usually still works, because a rule that locked you out normally names the port you were using rather than the terminal’s, but it is not a bypass: a drop broad enough to cover the terminal’s port closes that route too. From there or from the API, disable or narrow the drop that is blocking you, or reset: POST /api/v1/containers/{id}/firewall/reset. Adding an allow rule does not help, since drops are evaluated first.
Can firewall rules use domain names?
Section titled “Can firewall rules use domain names?”No. The firewall operates at the IP layer, so use CIDR notation only.
Troubleshooting
Section titled “Troubleshooting”Cannot connect after adding rules
Section titled “Cannot connect after adding rules”Solutions:
- List the rules:
GET /api/v1/containers/{id}/firewall/rules - Disable the blocking rule:
PATCH /api/v1/containers/{id}/firewall/ingress {"state": "disabled", "description": "..."} - Narrow the blocking drop so its port or source range no longer covers you, by deleting it and creating it again with the narrower range. An added allow rule cannot override it
- Reset the firewall:
POST /api/v1/containers/{id}/firewall/reset(removes all rules)
Rules not working
Section titled “Rules not working”Check:
- Rule state: must be
"state": "enabled" - Rule action: every drop is evaluated before every reject, and every reject before every allow, whatever order you created them in
- Protocol: a TCP rule will not block UDP traffic
Egress rules block package installation
Section titled “Egress rules block package installation”Leave the package ports out of the drop. An allow rule does not reopen what a drop covers, so narrow the drop until 443 and 53 fall outside it. The two rules below then record the ports you kept:
What’s next
Section titled “What’s next”Complete networking setup:
- Network Configuration → - Route traffic through proxies/VPNs
- VPN Inside a Container → - Run a WireGuard tunnel inside the container
- SSH Access → - Secure shell and SFTP
- IPv4 Management → - Dedicated IPs (coming soon)
Related security:
- Proxy Permissions → - Application-level access control
- Security Principles → - Hoody’s security philosophy
What this page covered:
- The firewall is enforced at host level, and containers cannot bypass it
- Ingress controls inbound traffic, egress controls outbound traffic
- Rules are evaluated in order, and the first match wins
- Three actions: allow, reject, drop
- The firewall is independent from Hoody Proxy Permissions