Reverse Proxy Setup for Self-Hosted AI Services on Ubuntu

2026-03-24 4 min read ubuntuself-hosted ai

A reverse proxy sits between the public internet and your self-hosted services. It terminates TLS, routes requests to the right application, and exposes only what should be public. For a self-hosted AI stack, it is not optional infrastructure. It is the part that determines what the outside world can reach.

Caddy is the simplest starting point. It handles TLS automatically using Let's Encrypt, requires minimal configuration for basic proxying, and has sensible security defaults. A minimal Caddyfile for a self-hosted app looks like:

yourdomain.com {
  reverse_proxy localhost:3000
}

That is enough to proxy traffic from port 443 with automatic TLS to a service listening on port 3000. If the service runs in Docker and publishes a port to the host, this works immediately.

Nginx requires more configuration but offers more control. If you are still deciding between Nginx, Caddy, and Traefik, see the Traefik vs Nginx comparison for a side-by-side breakdown. A basic proxy block for Nginx looks like:

server {
    listen 443 ssl;
    server_name yourdomain.com;
    ssl_certificate /etc/letsencrypt/live/yourdomain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/yourdomain.com/privkey.pem;

    location / {
        proxy_pass http://127.0.0.1:3000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }
}

Both options work. Caddy is faster to set up. Nginx is more predictable in complex configurations.

Docker networking adds a layer of consideration. If your AI service runs in Docker without publishing ports to the host, you cannot reach it via localhost from a host-level proxy. Options: publish the port with -p 3000:3000, run the proxy inside the same Docker network, or use Caddy as a Docker container in the same Compose file and reference the app by service name. Running the proxy as a container in the same network is often the cleanest option because it avoids exposing internal ports to the host unnecessarily.

For services that must not be public — admin interfaces, inference APIs, internal tooling — do not proxy them at all. If you must access them remotely, use an SSH tunnel rather than a public-facing proxy.

Security headers should be added at the proxy layer. At minimum: X-Content-Type-Options: nosniff, X-Frame-Options: DENY, and a Content-Security-Policy appropriate for your app. Caddy plugins and Nginx headers modules both handle this.

The reverse proxy is not the interesting part of your stack. That is its value. When it is configured well, you stop thinking about it. Configure it carefully once and let it do its job.

The most important design decision is deciding which services deserve a public route at all. Many self-hosted AI stacks accidentally expose too much. A public website or a user-facing app may need proxy access. Internal dashboards, vector databases, admin panels, and model APIs often do not. If they are only for operators, keep them on private networks and reach them through SSH tunnels, VPN, or Tailscale instead of publishing extra subdomains.

When configuring the proxy, think through four layers: DNS, TLS, upstream reachability, and headers. DNS must point the hostname at your server. TLS must terminate correctly. The proxy must be able to reach the upstream on the expected port. And headers must preserve the host, client IP, and any websocket upgrade requirements. Most proxy failures are just one of these layers misaligned.

It is also smart to add a few proxy-level protections from the beginning:

  • redirect HTTP to HTTPS
  • set conservative timeouts that fit your app behavior
  • limit public exposure to the minimum required routes
  • keep config files versioned so changes are traceable

If you use Nginx, document the relationship between server blocks and upstream apps carefully. If you use Caddy, keep the Caddyfile explicit enough that another operator can read it quickly and know what each hostname does. Readability matters because reverse proxies sit at a critical boundary in the stack.

A short example: if you host a static marketing site and a private AI dashboard on the same VPS, proxy the public site on the main domain, place the dashboard on a separate subdomain, and protect it with network restrictions or authentication. Do not assume obscurity is enough. Reverse proxies make exposure easy, which is precisely why they require deliberate configuration.

If you are mapping the broader workflow around this topic, it also helps to read How to Fix a 502 Bad Gateway on Ubuntu for Self-Hosted AI Apps and Why Your Self-Hosted AI Container Keeps Restarting.

This article is for general information only and should not be treated as professional advice.

Related transmissions
security
How to Secure a Self-Hosted AI Stack on Ubuntu
A practical baseline security guide for small self-hosted AI deployments on...
docker
Why Your Self-Hosted AI Container Keeps Restarting
A practical troubleshooting guide for self-hosted AI containers that loop,...
content strategy
A Simple Editorial Workflow for AI-Assisted SEO Posts
A practical editorial workflow for AI-assisted SEO content that keeps research,...
Newsletter

Stay in the signal

Practical guides on self-hosted AI, SEO, and open-source tools — no noise, no spam.

Follow via RSS →

Email signups open soon.