---
title: "Networking for Self-Hosters"
description: "Ports, DNS, firewalls, and private networks — the networking basics every self-hoster needs to know."
---

# Networking for Self-Hosters

You deployed a tool. It works on `localhost:3000`. You try to access it from your phone. Nothing. Welcome to networking.

This guide covers the **four things** standing between your server and the outside world.

## 1. Ports

Every network service listens on a **port** — a numbered door on your server. Some well-known ones:

| Port | Service |
|---|---|
| `22` | SSH |
| `80` | HTTP |
| `443` | HTTPS |
| `5432` | PostgreSQL |
| `3000–9000` | Where most self-hosted tools live |

When Docker maps `-p 8080:3000`, it's saying: "When traffic hits port 8080 on the host, send it to port 3000 inside the container."

```yaml
# In docker-compose.yml
ports:
  - "8080:3000"  # host:container
```

> ⚠️ **Never expose database ports** (5432, 3306, 27017) to the internet. Keep them internal to Docker networks.

## 2. DNS (Domain Name System)

DNS translates human-readable names to IP addresses:

```
plausible.yourdomain.com  →  203.0.113.42
```

### Setting Up DNS Records

In your domain registrar (Cloudflare, Namecheap, etc.):

| Type | Name | Value | What it does |
|---|---|---|---|
| **A** | `@` | `203.0.113.42` | Points root domain to your server |
| **A** | `plausible` | `203.0.113.42` | Points subdomain to your server |
| **CNAME** | `www` | `yourdomain.com` | Aliases `www` to root |
| **A** | `*` | `203.0.113.42` | Wildcard — catch-all for any subdomain |

> 💡 **Pro Tip:** A wildcard `*` A record + Caddy reverse proxy = unlimited subdomains with zero DNS management. Just add entries to your Caddyfile.

### DNS Propagation

After changing DNS records, it can take **5 minutes to 48 hours** to propagate globally. Use [dnschecker.org](https://dnschecker.org) to verify.

## 3. Firewalls (UFW)

A firewall controls which ports are open to the internet. On Ubuntu/Debian, use **UFW** (Uncomplicated Firewall):

```bash
# Check current status
ufw status

# Allow essential ports
ufw allow 22/tcp      # SSH — DON'T lock yourself out
ufw allow 80/tcp      # HTTP
ufw allow 443/tcp     # HTTPS

# Enable the firewall
ufw enable

# Deny everything else by default
ufw default deny incoming
ufw default allow outgoing
```

### The Golden Rule

Only open three ports to the internet: **22** (SSH), **80** (HTTP), **443** (HTTPS).

Your reverse proxy (Caddy/Nginx) handles port 80/443 and routes traffic internally to your containers. Individual tool ports (3000, 8080, etc.) should **never** be exposed publicly.

```
Internet → Port 443 → Caddy → Internal Docker Network → Your Tools
```

### Common Mistakes

**"I can't SSH into my server"** → You blocked port 22 before enabling UFW. Contact your hosting provider for console access.

**"My tool works locally but not remotely"** → Port 80/443 isn't open. Run `ufw allow 80/tcp && ufw allow 443/tcp`.

**"I opened port 8080 and got hacked"** → Never expose app ports directly. Use a reverse proxy instead.

## 4. Docker Networks

Docker creates isolated **networks** for your containers. By default, containers in the same `docker-compose.yml` can talk to each other by service name:

```yaml
services:
  app:
    image: myapp:latest
    depends_on:
      - db  # Can reach the database at "db:5432"

  db:
    image: postgres:16
    # No "ports:" = not accessible from outside Docker
```

### When to Create Custom Networks

If you need containers from **different** Compose files to communicate (e.g., a shared Caddy reverse proxy):

```yaml
# In your Caddyfile's docker-compose.yml
networks:
  proxy:
    external: true

# In your app's docker-compose.yml
networks:
  default:
    name: proxy
    external: true
```

Create the shared network first:

```bash
docker network create proxy
```

Now all containers on the `proxy` network can reach each other by service name — across different Compose files.

## Quick Reference

```bash
# See what's listening on which port
ss -tlnp

# Test if a port is open from outside
nc -zv your-server-ip 443

# See Docker networks
docker network ls

# Check DNS resolution
dig plausible.yourdomain.com
nslookup plausible.yourdomain.com
```

## Next Steps

→ [Reverse Proxies Explained](/concepts/reverse-proxies) — Route traffic from domains to containers
→ [SSL/TLS for Self-Hosters](/concepts/ssl-tls) — Encrypt your traffic
→ [Environment Variables & Secrets](/concepts/env-secrets) — Secure your configuration
