How to Use SWAG: The Beginner's Guide (First Compose, the CERT-EMAIL Lesson, Proxy Confs, and the Fail2ban Bonus)

CodexFuse Team · 2026-11-01T17:20:39.845Z · 10 min read

SWAG is LinuxServer's reverse proxy container: nginx + certbot + fail2ban in one box. This beginner guide covers your first compose file, certs, proxy confs, subdomains, basic auth, and fail2ban.

So you have a few services running on your server — maybe a media server, a file-sync app, a home dashboard — and you want to reach them from anywhere with proper HTTPS, without buying certificates or hand-configuring nginx. Learning how to use SWAG is the shortest path there. This LinuxServer SWAG tutorial walks you through the whole arc: your first compose file, the cert-email lesson, proxy confs for subdomains and subfolders, basic auth, and the fail2ban bonus that ships for free. If you are new to containers, read our Docker beginner guide first — SWAG assumes you can write a compose file, and everything below builds on it.

What Is SWAG, Exactly?

SWAG — Secure Web Application Gateway — is a single Docker container that acts as the front door for your homelab: it terminates TLS with automatically issued and renewed certificates, reverse-proxies each service to its own subdomain or subfolder, and ships fail2ban to block brute-force attempts. The mental model is one sentence: one public port 443, certificates that renew themselves, and every service gets its own address. Under the hood it is nginx plus a certbot client plus fail2ban, pre-wired so you configure one container instead of three services. Say it once and the rest of the guide makes sense: SWAG = nginx + automatic certs + a bouncer, in one box.

What Do You Need Before SWAG?

You need four things, and all four are cheap. First, a server you control — our guide on what is VPS hosting explains the hosting side if you are starting from zero. Second, a domain you control; a free DuckDNS subdomain works fine. Third, Docker and Docker Compose installed. Fourth, the ability to forward ports on your router, because SWAG's certificate validation needs to be reachable from the internet.

Your First SWAG Docker Compose File

A working SWAG docker compose setup takes about five minutes once the ports and DNS are ready. Here is the full arc:

  1. Create a folder, for example swag, and inside it a compose.yaml file.
  2. Paste the compose file below, replacing example.com with your domain.
  3. Forward ports 80 and 443 on your router to the machine running Docker.
  4. Run docker compose up -d and watch the logs with docker logs -f swag until you see the certificate issued.
  5. Open https://example.com in a browser — you should get SWAG's welcome page over a valid certificate.
services:
  swag:
    image: lscr.io/linuxserver/swag:latest
    container_name: swag
    cap_add:
      - NET_ADMIN
    environment:
      - PUID=1000
      - PGID=1000
      - TZ=Etc/UTC
      - URL=example.com
      - SUBDOMAINS=www,cloud
      - VALIDATION=http
      - [email protected]
      - STAGING=true
    volumes:
      - ./config:/config
    ports:
      - 443:443
      - 80:80
    restart: unless-stopped

Three notes on that file, each worth one read. The image is lscr.io/linuxserver/swag — that is the pull name the current official docs use. If an old tutorial tells you to pull linuxserver/letsencrypt, know that name is retired: SWAG is the renamed successor, and old guides predate the rename, so follow the new name. cap_add: NET_ADMIN is required so fail2ban can modify iptables rules. And STAGING=true is deliberate: your first run should hit the staging servers so a mistake does not burn you into a rate limit — flip it to false and recreate the container once everything works.

The CERT-EMAIL Lesson: EMAIL, CERTPROVIDER, and STAGING

Three env vars control your certificates, and beginners get tripped by all three. URL is the top domain you control; SUBDOMAINS is the comma-separated list the cert should cover, or exactly wildcard for a wildcard cert; VALIDATION is http or dns. The CERT-EMAIL lesson is the part people skip: EMAIL is optional for Let's Encrypt — it is used for cert expiration notifications — but it is required if you set CERTPROVIDER=zerossl, because ZeroSSL ties certificates to an account. SWAG defaults to Let's Encrypt; ZeroSSL has no rate limits and a web UI for cert management, at the cost of needing that account. And STAGING=true uses the ACME staging servers with much higher rate limits, so the official advice is to prove your setup on staging first, then go live. Certificates are valid for 90 days; the container checks them twice daily using ACME Renewal Information to pick the optimal renewal window, and renewal logs live at /config/log/letsencrypt.

Port 80 and 443: The Only Rule You Need

Port 443 is always required — that is the port your browser uses for HTTPS. Port 80 forwarding is required only when VALIDATION=http, because the ACME challenge reaches the container over port 80. If you use dns validation instead (with DNSPLUGIN and credentials in /config/dns-conf/), port 80 can stay closed. One legacy warning: older community guides mention a DHLEVEL variable for Diffie-Hellman key length — it does not exist in the current official docs at all, so ignore any guide that tells you to set it.

Proxy Confs: The .sample Ritual

SWAG proxy conf files are preset nginx configs for hundreds of popular apps, and enabling one is a ritual: samples live at /config/nginx/proxy-confs/ as inactive .sample files; you rename one to drop the .sample (for example nextcloud.subdomain.conf.sample becomes nextcloud.subdomain.conf) and restart the container. Nginx loads any file matching *.subdomain.conf or *.subfolder.conf at startup, so the rename is the entire activation step. Two rules matter: the proxied app must be on the same user-defined bridge network as SWAG so it resolves by container name, and any prerequisites are written at the top of each sample file — read them before renaming.

SWAG subdomain confSWAG subfolder conf
Address styleapp.example.comexample.com/app
Cert coverageEach subdomain needs SAN or wildcard certCovered by the main domain cert
App compatibilityWorks with nearly every appSome apps refuse to run under a path prefix
Beginner verdictStart here — fewer surprisesUse when an app breaks as a subdomain

The update rule is the part that bites later: proxy sample files get updated by new image releases, but your renamed, enabled confs are never touched — compare them against the new samples manually when you update. And if renaming confs one by one feels slow, the auto-proxy mod (DOCKER_MODS=linuxserver/mods:swag-auto-proxy) discovers local services and generates the nginx server blocks automatically.

Locking Doors With htpasswd Basic Auth

Some apps have no login of their own, so SWAG lets you gate them at the proxy. The exact official command creates the password file: docker exec -it swag htpasswd -c /config/nginx/.htpasswd username — and the -c flag is for the first user only, because it deletes any existing file; additional users run the same command without -c. Then uncomment the auth_basic lines in that app's conf and restart. If you want real single sign-on at the proxy instead, SWAG supports forward-auth to Authelia or Authentik. A SWAG Authelia pairing means Authelia handles the login while SWAG enforces it at the proxy — a separate identity service doing the login and SWAG asking it "is this visitor allowed". One mention, because basic auth covers most beginners and SSO is the graduation step.

Fail2ban: The Free Bonus

SWAG fail2ban integration is the feature people discover by accident: the container ships fail2ban pre-configured and enabled, with five default jails watching for bad behavior out of the box. That is why cap_add: NET_ADMIN matters — fail2ban writes iptables rules to drop attacking IPs, and it cannot do that without the capability. Here are the five jails you get for free:

JailWhat it watches
nginx-http-authRepeated failed basic-auth logins
nginx-badbotsKnown malicious bot user-agents
nginx-botsearchScans for common exploit paths (wp-admin, phpmyadmin, and friends)
nginx-denyRequests denied by nginx rules
nginx-unauthorized401/403 responses piling up from one IP

If you ever lock yourself out, the unban command is docker exec swag fail2ban-client unban <ip address>. Enable or disable other jails in /config/fail2ban/jail.local (never edit the .conf files — they get overwritten), and set DISABLE_F2B=true if you ever want fail2ban off entirely.

SWAG vs Caddy vs Pangolin: Draw the Lines

Three tools solve "expose my services safely" and beginners mix them up, so draw the lines once. Caddy is automatic everything in one file: you write a five-line Caddyfile and certificates, redirects, and the reverse proxy just happen — it is the simplicity champion, and our Caddy beginner guide covers it. SWAG is nginx power with preset proxy confs plus fail2ban in one container: more knobs, more app-specific presets, a built-in bouncer. Pangolin is the zero-trust tunnel lane: it exposes services without any public ports at all, which is a fundamentally different architecture — no 443 on your router, traffic flows through a tunnel instead. Choose Caddy for the shortest path, a SWAG reverse proxy when you want nginx's ecosystem and per-app presets, Pangolin when opening ports is not an option — our Pangolin beginner guide covers that tunnel lane in full.

SWAGCaddyPangolin
ArchitecturePublic 443 reverse proxyPublic 443 reverse proxyZero-trust tunnel, no open ports
Config stylenginx confs + presetsOne short CaddyfileDashboard + tunnels
Certscertbot, Let's Encrypt or ZeroSSLAutomatic, built inHandled by the tunnel
Brute-force defensefail2ban pre-configuredAdd your ownAccess control at the tunnel
Best fornginx control + app presetsFastest simple setupNo public ports allowed

Keeping SWAG Healthy

SWAG mostly runs itself, but two maintenance habits keep it boring in the good way. Certificates renew on their own — the container checks twice daily and uses ACME Renewal Information to pick the optimal renewal window, so a green padlock that never expires is the expected steady state. Container and config updates are the manual part: SWAG notes config changes in its changelog but never auto-applies them to your files, so after an update, review the changelog, and for any file you never modified, delete it and restart to get the fresh default. For proxy samples specifically, watch the linuxserver/reverse-proxy-confs commit history, since sample updates are not listed in the changelog — and remember your renamed confs are yours forever. If you are exposing services to the open internet, read our WireGuard beginner guide too: keeping admin panels behind a private VPN instead of the public web is the strongest hardening step you can take.

7 Beginner Mistakes With SWAG

MistakeWhat happensFix
Forgetting port 80 with http validationCertificate issuance failsForward port 80, or switch to dns validation
Skipping STAGING=true on the first runRepeated failures trigger Let's Encrypt rate limitsTest on staging first, then go live
Proxied app on the wrong Docker network502 Bad GatewayPut the app on SWAG's user-defined bridge network
Using -c on htpasswd for a second userWipes the existing password file-c is first-user-only; omit it after
Enabling the proxy conf before DNS propagatesValidation fails, cert never issuesConfirm DNS resolves before docker compose up
Editing fail2ban .conf filesChanges overwritten on updateOverride in jail.local and .local filters
Expecting renamed proxy confs to auto-updateStale configs silently fall behindDiff against new samples after each update

Frequently Asked Questions

Is SWAG free to use?

Yes. SWAG is an open-source LinuxServer container, and the certificates it issues come from Let's Encrypt or ZeroSSL, both free. The only costs are the ones you already have: a server and a domain.

Do I really need port 80 open?

Only if you use http validation. With dns validation and a DNSPLUGIN, port 80 can stay closed — the ACME challenge happens through DNS records instead.

Can I use Cloudflare DNS with SWAG?

Yes. Set VALIDATION=dns and DNSPLUGIN=cloudflare, then put your API token in /config/dns-conf/cloudflare.ini. Cloudflare's free DNS tier works fine for this.

Should I use SUBDOMAINS=wildcard or list my subdomains?

A wildcard needs dns validation, but then covers any subdomain you invent later. Listing subdomains works with http validation and is simpler to start with — you can switch to a wildcard whenever you outgrow the list.

How do I add a new service after the first run?

Put the service on SWAG's bridge network, rename its proxy conf to drop .sample, and restart the container (or enable the auto-reload mod). The existing certificate already covers the subdomain, so the new service is live in seconds.

Sources

Every fact in this guide was verified against official linuxserver sources fetched on 2026-11-01: the SWAG getting-started guide at docs.linuxserver.io/general/swag, the full README and Parameters table at github.com/linuxserver/docker-swag, the image page at hub.docker.com/r/linuxserver/swag, the hardening walkthrough at linuxserver.io/blog/securing-swag, the zero-trust and auto-proxy examples at linuxserver.io/blog/zero-trust-hosting-and-reverse-proxy-via-cloudflare-swag-and-authelia, and the 2020 rename announcement at linuxserver.io/blog/2020-08-21-introducing-swag.

Featured on Twelve Tools Launch Llama Tools Launch your MCP