Banner titled Securing Digital Infrastructure featuring SWAG and NGINX logos with a firewall shield icon on a digital circuit background.
The Holy Trinity of self-hosting: SWAG combines NGINX, Let's Encrypt, and Fail2ban into a single, hardened container.

Self-Hosting SWAG: Deploying Your Reverse Proxy in Docker (2026)

Forget Nginx Proxy Manager. Learn how to set up LinuxServer.io SWAG with CrowdSec, Authelia, and the new Dashboard Mod for a secure, automated reverse proxy stack.

In the last post, we walked through the architecture case for why every homelab needs a reverse proxy, and made the argument for why SWAG is the one Core Lab is standardizing on. If you missed that one and you're still choosing between Traefik, Caddy, NPM, and SWAG, go read it first - this post picks up assuming you've already made that call.

Right now we get our hands dirty. By the end of this post you'll have a working SWAG container issuing real, auto-renewing wildcard certificates, and your first self-hosted app running behind it on a clean subdomain. Exciting times lie ahead!

Learning Path • Reverse Proxy & Ingress

Mastering Reverse Proxies with SWAG

Deploy SWAG in Docker, configure Let's Encrypt certificates, integrate centralized Authelia MFA, and enforce Layer 7 CrowdSec AppSec (WAF) defense.

Lesson 2 of 5 40% Complete
Difficulty Curve Intermediate
Beginner Advanced
Tested in production on Linux & Docker CE
Core Lab Learning Track · Zero Tracking & Direct Implementation


Is This Guide For You?

This post is deployment only - no security hardening yet, no CrowdSec, no Authelia. That's coming in Parts 2 and 3 which you will get to pretty quick.

Today's goal is just: a working SWAG container with valid, auto-renewing certificates, proxying one real app. This becomes your baseline.

You're in the right place if you:

  • ✅ Already decided SWAG is your reverse proxy (see Part 1 if you haven't)
  • ✅ Have a domain you can point at Cloudflare
  • ✅ Are comfortable editing a docker-compose file and a couple of text configs
  • ✅ Want to understand what's happening, not just copy-paste a black box

This guide may not be for you if you:

❌ Want point-and-click setup (NPM)
❌ Need the easiest possible reverse proxy (NPM)
❌ Don’t want to touch config files (Probably NPM again)


Key Concepts of SWAG Deployment (Planning)

Although SWAG is just customized NGINX, there are some key concepts and underlying changes you should understand beforehand.

  1. Folder structure
  2. Where to edit files to effect changes
  3. What files to edit to enable services!
🎯
One quick note that'll save you a debugging session later: if your backend containers share a Docker network with SWAG, you can reference them by container name instead of IP - set $upstream_app jellyfin; works as long as jellyfin is on the same bridge network and Docker's internal DNS can resolve it.

Folder structure inside the SWAG container:

SWAG folder structure infographic showing the breakdown of the folders & files.
SWAG folder structure infographic.

Another way to look at the structure here, via a linux directory tree:

SWAG Linux Directory Tree

SWAG Container
│
├── nginx/
│ ├── nginx.conf
│ ├── proxy.conf
│ ├── ssl.conf
│ ├── resolver.conf
│ │
│ ├── site-confs/
│ │ ├── default.conf
│ │ ├── nextcloud.subdomain.conf
│ │ ├── jellyfin.subdomain.conf
│ │ └── ...
│ │
│ ├── proxy-confs/
│ │ ├── authelia.subdomain.conf.sample
│ │ ├── overseerr.subdomain.conf.sample
│ │ ├── sonarr.subdomain.conf.sample
│ │ ├── radarr.subdomain.conf.sample
│ │ └── ...
│ │
│ ├── dns-conf/
│ │ ├── cloudflare.ini
│ │ ├── duckdns.ini
│ │ ├── route53.ini
│ │ └── ...
│ │
│ ├── ssl/
│ │ ├── cert.pem
│ │ ├── privkey.pem
│ │ ├── fullchain.pem
│ │ └── ...
│ │
│ └── logs/
│ ├── access.log
│ ├── error.log
│ └── ...
│
├── fail2ban/
│ ├── jail.local
│ ├── fail2ban.log
│ └── filter.d/
│
├── www/
│ ├── index.html
│ ├── dashboard/
│ └── assets/
│
├── crontabs/
│ └── root
│
└── keys/
├── dhparams.pem
└── certificate-backups/


Installing & Deploying SWAG

Level 1: The Prerequisites

Before spinning up the container, get three things lined up:

1. A domain managed through Cloudflare. This matters specifically because Cloudflare's API lets us use the DNS-01 challenge - which means Let's Encrypt verifies you own the domain by checking a DNS TXT record, not by hitting port 80. Two big wins: you can issue a wildcard certificate (*.yourdomain.com), and you never have to open port 80 to the internet if you don't want to.

Cloudflare Protection for Homelabs: OPNsense, DDNS, & Wildcard Setup
Stop exposing your home IP. Learn how to use Cloudflare’s free tier with OPNsense to secure your homelab. Includes DDNS setup, wildcard CNAMEs, and IP masking.

2. Docker and Docker Compose installed on your host. If you're not there yet, that's a separate setup step. Follow my Definitive Docker Compose Setup Guide.👈👇

Docker & Compose on Ubuntu 24.04 / Debian 13: The 2026 Operations Manual
The definitive 2026 setup guide. Install Docker, setup Dockge & Dozzle, and master the “Apartment Model” of networking. Start self-hosting the right way.

3. A scoped Cloudflare API token - not your Global API Key. Scoped tokens limit the blast radius if one ever leaks.

To create it:

  1. Cloudflare Dashboard → My Profile → API Tokens
  2. Create Custom Token → name it something identifiable like SWAG-corelab
  3. Permissions: Zone → DNS → Edit and Zone → Zone → Read
  4. Zone Resources: Include → Specific Zone → yourdomain.com
  5. Create the token and copy it immediately — Cloudflare won't show it to you again

Review and Adjust DNS Records

  • Cloudflare will automatically create the following essential records:
    • A Record: Points your domain to Cloudflare’s IP addresses.
    • CNAME Records: For www and other subdomains.
    • Setup SSL settings so you can immediately make use of Cloudflare protection by:
      • Left sidebar - click SSL tab.
      • Set to SSL full strict. This is preferred and ensures your sites are locked into using and being served only via your SSL certificate. If you get an error when trying to access your site after enabling this, set it to SSL Full (not strict). If you have not setup your reverse proxy and SSL certificates yet, visit this CoreLab guide before continuing! That's a 3 part series of blog posts but parts 1 & 2 should do.
  • Crucially, enable Cloudflare Proxy (Orange Cloud): For every record (A, CNAME, MX, TXT), click the orange cloud icon. This activates Cloudflare’s proxy, which masks your server's IP address and provides protection against DDoS attacks, web scraping, and other malicious traffic.

Level 2: The SWAG Docker Compose File

Here's a lean, mods-free SWAG deployment - just the core container, nothing bolted on yet:

services:
  swag:
    image: lscr.io/linuxserver/swag:latest
    container_name: swag
    cap_add:
      - NET_ADMIN # Required for Fail2Ban+CrowdSec
    environment:
      - PUID=1000
      - PGID=1000
      - TZ=America/Toronto
      - URL=yourdomain.com
      - VALIDATION=dns
      - SUBDOMAINS=wildcard
      - DNSPLUGIN=cloudflare
      - [email protected]
    volumes:
      - /path/to/swag/config:/config
    ports:
      - "80:80"
      - "443:443"
    restart: unless-stopped
    # Optional: If you use a specific network
    # networks:
    #   - proxy_net

A quick rundown of what each variable is actually doing:

VariablePurpose
PUID / PGIDMatch these to your host user so SWAG's config files aren't owned by root
URLYour primary domain — this is what the cert gets issued for
VALIDATION=dnsTells SWAG to use DNS-01 instead of HTTP-01
SUBDOMAINS=wildcardRequests *.yourdomain.com instead of listing subdomains individually
DNSPLUGIN=cloudflareWhich DNS provider plugin certbot should use
EMAILWhere Let's Encrypt sends expiry warnings (shouldn't ever fire, but still)

Notice there's no DOCKER_MODS line here. SWAG's dashboard, CrowdSec bouncer, and auto-reload are all delivered as mods you bolt on with one environment variable - we're deliberately leaving that out for this post so you can see exactly what a bare, working deployment looks like before we layer anything on top of it in Part 2.

📄 Advanced SWAG Compose Example (Click to Expand)

services:
  swag:
    image: lscr.io/linuxserver/swag:latest
    container_name: swag
    cap_add:
      - NET_ADMIN # Required for Fail2ban/Crowdsec
# Use Host Networking OR MacVlan if you want real source IPs
# If using Bridge, ensure you setup 'Trusted Proxies' in NGINX
    network_mode: host
    #networks:
    #  docker_vlan42:
    #    ipv4_address: X.X.X.X (A LAN IP you pick)
    environment:
      - PUID=1000
      - PGID=1000
      - TZ=America/Toronto
      - URL=corelab.tech
      - VALIDATION=dns
      - SUBDOMAINS=wildcard
      - DNSPLUGIN=cloudflare
      - [email protected]
      - EXTRA_DOMAINS=blah.tech,personal.blog #optional
# THE POWER UP: Chain mods with a pipe | character
      #- DOCKER_MODS=linuxserver/mods:swag-dashboard|linuxserver/mods:swag-crowdsec|linuxserver/mods:swag-auto-reload # Disabled for now
    volumes:
      - /yourlocation/swag:/config
    ports:
      - 443:443
      - 80:80    # Optional if using DNS validation, but good for redirects
      - 81:81    # The New Dashboard Port
    restart: unless-stopped
        

Choosing a Network Mode

This decision affects whether SWAG (and later, Fail2ban/CrowdSec) can see the real IP address of visitors, so it's worth understanding before you deploy - not after.

ModeReal Client IPSetup ComplexityBest For
Bridge (default)❌ No — shows Docker gateway IP unless you configure trusted proxiesLowGetting started quickly
Host✅ YesLowSimplicity, less network isolation
Macvlan✅ YesMediumDedicated LAN IP, best isolation

For this first deployment, bridge mode is fine - it's the fastest way to get something working, and you can always migrate to macvlan later once you're adding CrowdSec/Fail2ban in Part 2 and want real IP visibility for accurate banning. If you already know you want macvlan, the compose changes to add a networks section at the top or bottom. Canonically it's supposed to be at the bottom:

networks:
  swag_vlan:
    driver: macvlan
    driver_opts:
      parent: eth0
    ipam:
      config:
        - subnet: "192.168.42.0/24"
          gateway: "192.168.42.1"

...with a static ipv4_address assigned to the SWAG service instead of the ports: mapping.

For more on Docker Compose Networking, please visit my Definitive Docker Compose Guide - Networking Section.

Configuring the Cloudflare DNS Plugin

SWAG needs to generate its default folder structure before you can edit anything, so this is a short back-and-forth:

  1. Run docker compose up -d once, just to let SWAG generate its config skeleton
  2. Stop it: docker compose down
  3. Edit the DNS plugin config: nano /path/to/swag/dns-conf/cloudflare.ini
  4. Add your token:
# Comment out the email/global-key lines if present, and use only this:
dns_cloudflare_api_token = your_token_here

Save and exit (Ctrl+O, Ctrl+X).

Screenshot of the all the DNS config files inside SWAG's folder /dockers/swag/dns-conf/
You have a LOT of DNS options to utilize!

Level 3: First Boot: Bringing SWAG Online

Now for real:

docker compose up -d
docker compose logs -f swag

Watch for this sequence in the logs:

dns validation via cloudflare plugin is selected
Certificate exists; parameters unchanged; starting nginx
The cert does not expire within the next day. Letting the cron script handle the renewal attempts overnight (2:08am).
**** Configuring CrowdSec nginx Bouncer ****
**** Applying the SWAG dashboard mod... ****
**** Adding goaccess to package install list ****
**** libmaxminddb already installed, skipping ****
**** Applied the SWAG dashboard mod ****
**** Successfully configured CrowdSec nginx Bouncer ****
[pkg-install-init] **** Installing all mod packages ****
fetch http://dl-cdn.alpinelinux.org/alpine/v3.22/main/x86_64/APKINDEX.tar.gz
fetch http://dl-cdn.alpinelinux.org/alpine/v3.22/community/x86_64/APKINDEX.tar.gz
(1/17) Installing gettext-envsubst (0.24.1-r0)
(2/17) Installing libgomp (14.2.0-r6)
(3/17) Installing gettext-libs (0.24.1-r0)
(4/17) Installing gettext (0.24.1-r0)
(5/17) Installing goaccess (1.9.4-r0)
(6/17) Installing lua5.1-libs (5.1.5-r13)
(7/17) Installing lua5.1 (5.1.5-r13)
(8/17) Installing lua-resty-http (0.17.2-r0)
(9/17) Installing luajit (2.1_p20240815-r1)
(10/17) Installing lua-resty-lrucache (0.13-r1)
(11/17) Installing lua-resty-core (0.1.31-r0)
(12/17) Installing nginx-mod-http-lua (1.28.0-r3)
(13/17) Installing lua-resty-string (0.15-r0)
(14/17) Installing lua-sec (1.3.2-r0)
(15/17) Installing lua5.1-socket (3.1.0-r1)
(16/17) Installing lua5.1-sec (1.3.2-r0)
(17/17) Installing lua5.1-cjson (2.1.0-r11)
Executing busybox-1.37.0-r19.trigger
OK: 197 MiB in 238 packages
[custom-init] No custom files found, skipping...
[ls.io-init] done.

That last line means you're live, setup was successful! If it stalls out waiting on validation, it almost always means the Cloudflare token wasn't saved before the first real attempt - double check cloudflare.ini, then docker compose restart swag. Also be paranoid and check permissions on your files with ls -l.

Verifying Your Certificate

Point a browser at https://yourdomain.com and check the padlock - you should see a valid Let's Encrypt cert with no warnings. For a more thorough check, run your domain through Qualys SSL Labs. SWAG's default cipher configuration should score an A or B+ out of the box, with zero manual tuning.

Detailed review showing key criteria for scoring an A+, Post-quantum encryption etc
Detailed review showing key criteria for scoring an A+, Post-quantum encryption etc

If you want to go beyond basic SSL validation and configure modern TLS 1.3 ciphers, HSTS, and post-quantum encryption to score a perfect A+ on SSL Labs, follow my dedicated guide: How to Harden SWAG or NGINX for an A+ Security Rating.

🎉
Checkpoint
If you've reached this point... Congratulations - you've already completed the hardest part of the series!!! Go grab a drink, quick break! Great spot to take pause.

You've got:
✅ SWAG is running, ✅ Your certificate is valid, ✅ HTTPS works, ✅ You're ready to proxy applications

Take a moment to appreciate that. You've built the foundation that every secure, internet-facing homelab needs.

From here on out, things get considerably easier. The remaining work is largely a matter of enabling services, fine-tuning your configuration, and layering on additional security features.

Reverse Proxying Your First Container

This is the payoff step - let's put a real app behind SWAG. We'll use Jellyfin as the example, but the pattern is identical for anything else.

  1. Navigate to the sample configs: cd /path/to/swag/nginx/proxy-confs/
  2. Find the sample for your app: ls | grep jellyfin
  3. Rename it to activate it: mv jellyfin.subdomain.conf.sample jellyfin.subdomain.conf
  4. Open it and check the upstream target:
set $upstream_app jellyfin;
set $upstream_port 8096;
set $upstream_proto http;

If your Jellyfin container is named jellyfin and shares a Docker network with SWAG, you're done! Docker's internal DNS resolves the container name automatically. If it's running elsewhere on your LAN, point it at the IP instead:

set $upstream_app 192.168.1.50;
set $upstream_port 8096;
set $upstream_proto http;
  1. Save the file, then reload SWAG since we don't have the auto-reload mod running yet:

It should end up looking something like this but NOTE: I have mine set to IP because I isolate SWAG onto a dedicated DMZ VLAN with macvlan routing (covered in my 2026 Homelab Networking Stack), and QUIC enabled so that will also look a touch different.

## Version 2025/07/18
# make sure that your jellyfin container is named jellyfin
# make sure that your dns has a cname set for jellyfin
# if jellyfin is running in bridge mode and the container is named "jellyfin", the below config should work as is
# if not, replace the line "set $upstream_app jellyfin;" with "set $upstream_app <containername>;"
# or "set $upstream_app <HOSTIP>;" for host mode, HOSTIP being the IP address of jellyfin
# in jellyfin settings, under "Advanced/Networking" add subdomain.mydomain.tld as a known proxy

server {
    listen 443 ssl;
    listen 443 quic;
    listen [::]:443 ssl;
    listen [::]:443 quic;

    server_name jf.*;

    # Advertise HTTP/3 to the client
    add_header Alt-Svc 'h3=":443"; ma=86400';

    # Enable QUIC specifics
    http3 on;
    quic_retry on; # Security: prevents IP spoofing/reflection attacks

    include /config/nginx/ssl.conf;

    client_max_body_size 128M;

    location / {
        include /config/nginx/proxy.conf;
        include /config/nginx/resolver.conf;
        set $upstream_app 10.0.0.49;
        set $upstream_port 8096;
        set $upstream_proto http;
        if ($http_user_agent ~ Web0S) {
            add_header Access-Control-Allow-Origin "luna://com.webos.service.config" always;
#### More config truncated below
docker compose restart swag
  1. Visit (from outside your LAN like on your phone) https://jellyfin.yourdomain.com - you should land straight on your app, over a valid HTTPS connection, without ever touching port 8096 directly.

For an exhaustive walkthrough covering WebSocket streaming, subpath vs subdomain routing, client buffering, and alternative proxies like NPM and Traefik, head over to my dedicated Jellyfin HTTPS Reverse Proxy Guide.


Best SWAG Docker Mods - Day 2 Ops

This is what makes SWAG even more powerful. Let's start simple and add just the SWAG dashboard mod first. Go back into your docker compose file, and add or uncomment the line if it's already there DOCKER_MODS=linuxserver/mods:swag-dashboard and add port 81 if it's not in your port list too, then restart SWAG again with command docker compose restart swag.

If you want to view the entire collection of SWAG mods & official documentation from Linuxserver.io, here it is!

📊 The Dashboard (Port 81)

Go to http://YOUR-SERVER-IP:81. You will see the GoAccess Dashboard. This parses your access logs in real-time.

  • What to look for: All kinds of stats shown in the 2nd pic below, but that may take time to generate if you don't have a lot of traffic yet. Prob best to example it in a couple days once you've had your reverse proxy serving an URL publicly.
  • Important part: The dashboard loaded! That's the goal.
Can really dig into who is being naughty or nice!

CloudFlare Real-IP Mod

Once again, modify your docker-compose and add the mod in along the list behind your dashboard mod linuxserver/mods:swag-cloudflare-real-ip and don't forget to use the | pipe in an array (no spaces!) for multiple mods. Welcome to advanced SWAG config 😉.

The file gets placed in your persistent data, at /config/nginx/cf_real-ip.conf

To enable nginx to read the IPs from this file, you need the following in your nginx.conf (http section):

real_ip_header X-Forwarded-For;
real_ip_recursive on;
include /config/nginx/cf_real-ip.conf;

This mod also tries to detect the real ip from the interfaces in the container.

The GEOIP Lookup Panel & Geofencing Info

It's even got a geo-map to see who is reaching out and touching you!

Now, if you want the nice GEO LOCATION / correlation panel, you have to enable two more SWAG mods. linuxserver/mods:swag-cloudflare-real-ip|linuxserver/mods:swag-maxmind and you add those with a pipe after your already existing 1st mod - the dashboard itself. Here's the pipe itself, can copy but it's above splitting the two mods as well. | . This gives you the beautiful visual above, and the ability to geo-fence with SWAG itself!

It won't automatically work right off the bat, there's some config needed here.

Obtain a MaxMind GeoLite2 Account & License Key

SWAG (and OPNsense) use MaxMind databases for GeoIP functionality. You'll need a free GeoLite2 account:

  1. Go to the MaxMind Signup Page.
  2. Fill out the form to create a free account. (You might need to wait for approval).
  3. Once your account is active, log in to your MaxMind account.
  4. Navigate to "Manage License Keys" (often found under "My Account" or similar).
  5. Generate a new license key. Copy this key immediately and store it securely. MaxMind may only show it to you once. Give it a descriptive name like "OPNsense Key". Select "No" when asked if you use geoipupdate.

Now go back into your docker compose and add these environment variables to your SWAG config nano docker-compose.yaml :

  - MAXMINDDB_LICENSE_KEY=your licence key, it's long
  - MAXMINDDB_USER_ID=a number here, yours
  1. Recreate the container to apply the changes with docker compose up -d --force-recreate swag
  2. Add the following line to /swag/nginx/nginx.conf under the http section:
include /config/nginx/maxmind.conf;

I did not enable geo-fencing in SWAG, as I control that upstream at CloudFlare level & in my firewall (OPNsense). You can follow LinuxServer.IO's official mod instructions to finish the config if you'd like to do that.👇

GitHub - linuxserver/docker-mods at swag-maxmind
Documentation and Examples of base container modifications - linuxserver/docker-mods

At the end of those mods, here's what my entire mods line looks like:

- DOCKER_MODS=linuxserver/mods:swag-dashboard|linuxserver/mods:swag-cloudflare-real-ip|linuxserver/mods:swag-maxmind

And here's what my http portion of the NGINX config looks like with those additions:

http {
    # Includes mapping of file name extensions to MIME types of responses
    # and defines the default type.
    include /etc/nginx/mime.types;
    default_type application/octet-stream;

    # ✅ REAL IP FIX (correct location)
    real_ip_header X-Forwarded-For;
    real_ip_recursive on;
    include /config/nginx/cf_real-ip.conf;
    # Maxmind Mod
    include /config/nginx/maxmind.conf;

    # Name servers used to resolve names of upstream servers into addresses.
    # It's also needed when using tcpsocket and udpsocket in Lua modules.
    #resolver 1.1.1.1 1.0.0.1 2606:4700:4700::1111 2606:4700:4700::1001;
    include /config/nginx/resolver.conf;

# Remainder of file truncated for the guide

What's Next

You've now got a bare, working reverse proxy with real certificates and one app live behind it. That's a legitimately solid foundation - plenty of homelabs stop right here and that's fine. You've built the front door and locked it.

But if you're exposing anything to the internet, the next two posts in this series are where SWAG earns the "Secure" in its name:

  • Part 3: Layered Security with SWAG, CrowdSec & Authelia - bolting on the CrowdSec bouncer, the traffic dashboard, and Authelia for MFA on your riskier apps
    • This becomes real-time threat intelligence and finally transform it from a reverse proxy to a hardened internet facing gateway.
See you in Part 3!

Troubleshooting Common Deployment Issues

ProblemLikely CauseFix
Certificate request failsBad or under-scoped Cloudflare tokenRecheck token permissions (DNS:Edit, Zone:Read)
502 Bad GatewayWrong upstream container name or portVerify the container name and that it's on the same Docker network
Real client IP missing in logsBridge mode without trusted proxy configSwitch to host/macvlan, or configure trusted proxies
Config changes don't take effectNo auto-reload mod yet (expected right now)docker compose restart swag after any config edit
SWAG won't start at allPort 80/443 already bound by another service`sudo ss -tulpn

Frequently Asked Questions

Can I use SWAG without Cloudflare?

Yes, but Cloudflare is the option I recommend throughout this guide because it supports the DNS-01 certificate challenge through its API. That allows SWAG to issue wildcard Let's Encrypt certificates without exposing port 80 to the internet.

SWAG supports many other DNS providers—including Route53, DuckDNS, DigitalOcean, Gandi, and more—but the configuration will vary depending on your provider.

Why does SWAG use the container name instead of an IP address?

When SWAG and your applications share the same Docker bridge network, Docker automatically provides an internal DNS service. Instead of hardcoding IP addresses—which can change when containers are recreated—you can simply reference the container by name (for example, jellyfin). Docker resolves that name automatically, making your configuration more portable and easier to maintain.

Why does this guide use the DNS-01 challenge instead of HTTP-01?

The DNS-01 challenge verifies domain ownership by creating a temporary DNS TXT record rather than requiring Let's Encrypt to reach your web server.

This provides several advantages:

  • Supports wildcard certificates (*.yourdomain.com)
  • Doesn't require exposing port 80
  • Continues working even if your web server is offline
  • Generally fits homelab environments much better

Do I need to expose both ports 80 and 443?

Not necessarily.

For DNS-01 validation, only port 443 is typically required for serving your applications.

Some users still choose to expose port 80 so HTTP requests can automatically redirect to HTTPS, but it's no longer required for certificate issuance when using DNS validation. Because I host a website where I want users to easily access, I have port 80 open and it redirects to 443.

Should SWAG run in Bridge or Macvlan mode?

Both work well.

Bridge mode is simpler and is what I recommend for first-time deployments. Docker's built-in DNS allows SWAG to communicate with your containers by name, making configuration straightforward.

Macvlan becomes attractive later when you want SWAG, Fail2ban, or CrowdSec to see real client IP addresses instead of Docker bridge addresses.

You can always migrate later without rebuilding your entire stack.

Can SWAG proxy containers running on another Docker host?

Yes.

SWAG doesn't care whether the backend service is local or remote.

As long as SWAG can reach the application over your network, you simply point the upstream host to the server's IP address or DNS name instead of a Docker container name.

How do I add another application behind SWAG?

This is one of SWAG's biggest strengths.

Simply:

  1. Find the matching sample configuration in /config/nginx/proxy-confs/
  2. Rename it to remove the .sample extension
  3. Update the upstream container name or IP address if needed
  4. Restart or reload SWAG

LinuxServer.io maintains hundreds of ready-made configurations, meaning you rarely have to write your own reverse proxy configuration from scratch.

How do I know if my SSL certificate was created successfully?

The easiest way is to visit your domain in a browser and verify that:

  • HTTPS loads successfully
  • The browser shows a valid padlock
  • The certificate is issued by Let's Encrypt

For a deeper analysis, use SSL Labs' free SSL Test, which evaluates protocol support, cipher suites, certificate configuration, and overall security.

Why can't SWAG connect to my backend container?

The most common causes are:

  • The application isn't running.
  • SWAG and the application aren't on the same Docker network.
  • The upstream container name is incorrect.
  • The application is listening on a different port.
  • A firewall is blocking communication.

If both containers share the same Docker bridge network, using the container name instead of an IP address is usually the simplest solution.

What happens when my Let's Encrypt certificate expires?

Nothing—assuming everything is configured correctly.

SWAG automatically renews certificates before they expire using scheduled cron jobs. As long as your DNS provider credentials remain valid and your domain continues resolving correctly, certificate renewal happens automatically without manual intervention.

Can I migrate from NGINX Proxy Manager to SWAG?

Absolutely.

Most migrations involve recreating each proxy host using SWAG's sample configurations and updating your DNS records once everything has been tested.

In most cases, you can migrate one application at a time, allowing both platforms to coexist temporarily while you move services over.

Share this article
Share

Written by

Core Lab Joe
Core Lab Joe
Dad, IT guy, Self-Hosting Evangelist! https://www.reddit.com/r/corelabtech/
corelab.tech
Facebook
Mastodon

On this page

What's Next?