SSD Nodes Learn Hosting plans →
가이드 Matt Connor작성자 Matt Connor

Traefik v3로 Docker Compose 앱 5개 배포하기

하나의 IP와 443 포트에서 Traefik v3를 사용하여 5개의 애플리케이션을 운영하는 방법을 설명합니다. Host 규칙 설정부터 자동 Let's Encrypt TLS 적용, 그리고 인증서 발급을 차단하는 acme.json 권한 설정 오류 해결법까지 실무적인 가이드를 제공합니다.

단일 IP, 5개의 애플리케이션, 1개의 443 포트

VPS에는 단일 공인 IPv4 주소와 단일 TCP 443 포트가 있습니다. 여기에 Gitea, 애플리케이션의 스테이징 복사본, 내부 대시보드, 상태 페이지, 웹훅 수신기를 모두 올리고 싶다면 5개의 호스트 이름을 하나의 서버에서 운영해야 합니다. 리버스 프록시는 :80 및 :443 포트를 점유하고 모든 요청의 Host 헤더를 읽어 적절한 컨테이너로 전달하는 프로세스입니다. Traefik은 이 역할을 수행하며, 사용자가 직접 certbot을 실행할 필요 없이 각 호스트 이름에 대한 인증서를 자동으로 발급하고 갱신합니다. Nginx와 Caddy 또한 동일한 5개의 호스트 이름을 완벽하게 처리할 수 있으므로, 아직 선택 전이라면 인증서 처리 방식과 애플리케이션별 설정 비용을 기준으로 세 가지 프록시를 비교해 보는 것이 좋습니다.

Traefik과 Nginx의 server {} 블록을 구분 짓는 핵심은 설정이 어디에서 오는가입니다. Nginx는 파일을 수정하고 리로드해야 하며, 인증서 수명 주기는 별도의 작업으로 남습니다. 이는 certbot을 사용하여 Nginx에서 Let's Encrypt 인증서를 발급할 때 따르는 워크플로우와 같으며, 갱신 타이머는 웹 서버 외부에서 별도로 동작합니다. 반면 Traefik의 Docker 프로바이더는 Docker 이벤트 스트림을 감시하고 컨테이너의 레이블(labels)을 읽습니다. Host() 규칙 레이블이 포함된 컨테이너를 시작하면 1초 이내에 라우팅이 가능해지고, 컨테이너를 중지하면 경로는 즉시 사라집니다. 이것이 바로 함정이기도 합니다. 설정이 5개의 컨테이너 레이블에 분산되어 존재하기 때문에, 레이블을 잘못 입력하면 오류 메시지 없이 컨테이너가 라우팅 대상에서 제외되며 Traefik은 아무런 경고도 하지 않습니다.

네 가지 핵심 개념

  • Entrypoints는 수신 대기 중인 소켓입니다. 두 가지를 정의합니다. web는 :80에서, websecure는 :443에서 대기합니다.
  • Routers는 요청(Host(...))을 매칭하여 서비스에 연결합니다. 인증서는 tls.certresolver을 통해 라우터 단위로 요청됩니다.
  • Services는 백엔드이며, Docker 네트워크 내부에서 컨테이너와 해당 컨테이너가 수신 대기하는 포트를 의미합니다.
  • Middlewares는 라우터와 서비스 사이에 위치하며 기본 인증, IP 허용 목록, 헤더 재작성, 리다이렉트 등을 수행합니다.

이 네 가지 개념은 사용자가 직접 수동으로 작성해야 할 작업을 Traefik이 명명한 것입니다. 라우터는 server_name에 해당하고, 서비스는 proxy_pass 대상이며, 미들웨어는 nginx 리버스 프록시 서버 블록을 한 줄씩 작성할 때 직접 설정하는 헤더 및 인증 지시어와 같습니다.

정적 설정(entrypoints, providers, ACME)은 Traefik 명령줄이나 traefik.yml을 통해 전달되며, 이를 변경하려면 Traefik을 재시작해야 합니다. 동적 설정(routers, services, middlewares)은 컨테이너 레이블을 통해 전달되며 실시간으로 다시 불러옵니다. 이 두 가지를 혼동하는 것이 "설정 플래그가 작동하지 않는다"는 문제가 발생하는 주된 원인입니다.

Compose 파일

proxy라는 이름의 공유 Docker 네트워크가 중추 역할을 합니다. Traefik은 두 컨테이너가 모두 이 네트워크에 연결되어 있을 때만 해당 컨테이너에 접근할 수 있습니다.

name: edge

networks:
  proxy:
    name: proxy

services:
  traefik:
    image: traefik:v3.7
    restart: unless-stopped
    command:
      - --providers.docker=true
      - --providers.docker.exposedByDefault=false
      - --providers.docker.network=proxy
      - --entryPoints.web.address=:80
      - --entryPoints.websecure.address=:443
      - --entryPoints.web.http.redirections.entryPoint.to=websecure
      - --entryPoints.web.http.redirections.entryPoint.scheme=https
      - --certificatesresolvers.le.acme.email=you@example.com
      - --certificatesresolvers.le.acme.storage=/letsencrypt/acme.json
      - --certificatesresolvers.le.acme.tlschallenge=true
      # while you iterate, point at staging so a mistake costs nothing:
      # - --certificatesresolvers.le.acme.caserver=https://acme-staging-v02.api.letsencrypt.org/directory
      - --api.dashboard=true
      - --log.level=INFO
      - --accesslog=true
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - ./letsencrypt:/letsencrypt
    networks:
      - proxy
    labels:
      - traefik.enable=true
      - traefik.http.routers.dashboard.rule=Host(`traefik.example.com`)
      - traefik.http.routers.dashboard.entrypoints=websecure
      - traefik.http.routers.dashboard.tls.certresolver=le
      - traefik.http.routers.dashboard.service=api@internal
      - traefik.http.routers.dashboard.middlewares=dashboard-auth
      - traefik.http.middlewares.dashboard-auth.basicauth.users=admin:$$apr1$$REPLACE$$THIS

  gitea:
    image: gitea/gitea:1  # major-only pin keeps this demo copy-pasteable; pin an exact release in production
    restart: unless-stopped
    volumes:
      - ./gitea:/data
    networks:
      - proxy
    labels:
      - traefik.enable=true
      - traefik.http.routers.gitea.rule=Host(`git.example.com`)
      - traefik.http.routers.gitea.entrypoints=websecure
      - traefik.http.routers.gitea.tls.certresolver=le
      - traefik.http.services.gitea.loadbalancer.server.port=3000

docker compose up -d를 수행한 뒤 docker compose logs -f traefik를 수행합니다. 추가되는 각 애플리케이션은 gitea 블록을 복사하여 고유한 라우터 이름, Host(), 내부 포트를 설정합니다. TLS와 백업을 포함한 Docker 기반 Nextcloud 설치도 같은 방식으로 구성합니다. 기존에 게시된 포트를 제거하고 proxy에 연결한 뒤, 라우터 레이블이 호스트 이름과 인증서를 처리하도록 설정하십시오.

다음 다섯 가지 세부 사항은 매우 중요합니다.

exposedByDefault=false 설정을 사용하면 traefik.enable=true 레이블이 지정되기 전까지 컨테이너가 Traefik에 노출되지 않습니다. 이 설정을 생략하면 확인을 위해 실행한 일회성 postgres 컨테이너를 포함하여, 실행하는 모든 컨테이너에 대해 라우팅이 생성됩니다.

providers.docker.network=proxy는 컨테이너가 여러 네트워크에 연결되어 있을 때 Traefik이 사용할 네트워크를 지정합니다. 이를 생략하면 Traefik이 잘못된 컨테이너 IP를 선택할 수 있으며, 이는 애플리케이션 오류처럼 보이는 502 오류로 나타납니다.

loadbalancer.server.port=3000는 컨테이너 내부의 포트입니다. 예를 들어 Gitea는 내부에서 3000번 포트를 사용합니다. 어떤 애플리케이션 컨테이너도 포트를 외부로 게시하지 않으며, 오직 Traefik만 포트를 게시한다는 점에 유의하십시오.

web 엔트리포인트의 리다이렉트는 기본적으로 영구적(permanent)이므로, 일반 텍스트 GET 요청(브라우저가 보내는 요청)은 HTTPS로 301 리다이렉트됩니다. Traefik은 POST와 같은 다른 메서드에 대해서는 308을 응답하여 요청 메서드가 유지되도록 합니다. 80번 포트는 항상 열려 있어야 합니다. ACME HTTP 챌린지와 호스트 이름만 입력하는 사용자를 위해 필요하기 때문입니다.

basic-auth 해시에서 $$이 두 번 반복되는 것은 오타가 아니라 Compose의 이스케이프 처리입니다. htpasswd -nbB admin 'your-password'(apache2-utils 패키지)으로 해시를 생성한 뒤, 모든 $를 두 번씩 입력하십시오.

인증서와 acme.json 함정

tlschallenge=true은 TLS-ALPN-01을 선택합니다. Let's Encrypt가 443 포트로 서버에 접속하면 Traefik이 TLS 핸드셰이크 내부에서 챌린지에 응답합니다. 대안으로 80 포트를 사용하는 HTTP-01 방식이 있습니다. Traefik의 command: 목록에서 tlschallenge 줄을 다음 두 줄로 교체하십시오.

      - --certificatesresolvers.le.acme.httpchallenge=true
      - --certificatesresolvers.le.acme.httpchallenge.entrypoint=web

둘 다 사용 가능합니다. 두 방식 모두 호스트 이름에 대한 공용 DNS가 이미 VPS를 가리키고 있어야 하며, 인증 기관이 해당 이름을 해석하고 외부에서 접속할 수 있어야 합니다. 먼저 A(및 AAAA) 레코드를 생성하고 dig +short git.example.com로 확인한 다음 Traefik을 시작하십시오.

이제 많은 사용자가 저녁 시간을 허비하게 만드는 함정에 대해 설명합니다. Traefik은 ACME 계정 키와 발급된 모든 인증서를 하나의 acme.json 파일에 보관합니다. 이 파일이 그룹이나 다른 사용자에게 읽기 권한이 부여된 상태라면, Traefik은 이와 유사한 로그를 남기고 리졸버를 중단합니다. Traefik은 계속 실행되고 라우팅도 수행하지만, 인증서 요청이 이루어지지 않아 모든 호스트 이름이 TRAEFIK DEFAULT CERT 상태로 서비스됩니다.

ERR The ACME resolve is skipped from the resolvers list error="unable to get ACME account: permissions 644 for /letsencrypt/acme.json are too open, please use 600" resolver=le

가장 깔끔한 해결책은 위에서 언급한 방식입니다. 디렉터리를 바인드 마운트하여 Traefik이 올바른 권한 모드로 파일을 직접 생성하게 하십시오. 만약 touch을 사용하여 acme.json을 직접 생성했다면, umask 설정으로 인해 권한이 644로 지정되었을 것입니다. 호스트에서 다음 명령으로 수정하십시오.

chmod 600 ./letsencrypt/acme.json
docker compose restart traefik

해당 디렉터리를 애플리케이션 볼륨과 함께 백업하십시오. 한 번 잃어버리는 것은 복구할 수 있고 인증서도 재발급받으면 되지만, 재배포할 때마다 디렉터리를 삭제하면 7일마다 동일한 호스트 이름 세트에 대해 5개까지만 발급 가능한 인증서 제한에 걸리게 됩니다.

설정을 반복하는 동안에는 스테이징 CA를 사용하십시오. caserver 줄의 주석을 해제하고 모든 경로가 작동하는지 확인한 뒤, 주석 처리하고 acme.json을 삭제하여 프로덕션 인증서를 새로 요청하십시오. Let's Encrypt 프로덕션 환경은 동일한 호스트 이름 세트에 대해 주당 5개의 중복 인증서 발급을 허용하며, 동일한 이름에 대한 반복적인 검증 실패 시 속도를 제한합니다. 스테이징 환경은 신뢰할 수 없는 인증서를 발급하므로 브라우저에서 경고가 표시되지만, 이 경고는 설정이 정상적으로 작동했다는 신호이며 제한도 훨씬 느슨합니다.

대시보드는 제어 인터페이스이지 데모가 아닙니다

대부분의 퀵스타트 가이드는 --api.insecure=true 설정을 사용하는데, 이는 인증 없이 8080 포트로 대시보드를 노출합니다. 공인 IP가 할당된 서버에서 이 설정을 사용하면 스캔을 시도하는 누구에게나 라우팅 토폴로지, 호스트 이름, 미들웨어 이름, 백엔드 포트 정보가 그대로 노출됩니다.

위의 traefik 서비스에 적용된 레이블은 이에 대한 대안입니다. 대시보드를 다른 일반적인 애플리케이션처럼 실제 호스트 이름과 TLS를 사용하여 basicauth 뒤에서 라우팅하는 방식입니다. service=api@internal는 라우터와 Traefik의 내장 API를 연결하는 역할을 합니다. 여기에 왼쪽에서 오른쪽 순으로 적용되는 IP 허용 목록을 추가하여 보안을 더욱 강화하십시오. 사무실 IP 주소가 유동적이라면, 동일한 VPS에 직접 호스팅하는 WireGuard VPN에서 할당하는 서브넷 범위로 설정하고 터널을 통해서만 대시보드에 접근하도록 구성하십시오.

- traefik.http.middlewares.office.ipallowlist.sourcerange=10.0.0.7/32
- traefik.http.routers.dashboard.middlewares=office,dashboard-auth

다섯 개 이상의 애플리케이션이 각자의 계정을 필요로 하게 되면 하나의 basicauth 비밀번호를 공유하는 방식은 더 이상 방어 수단이 될 수 없습니다. 동일한 미들웨어 슬롯에 forwardauth을 적용하면 인증 권한을 직접 호스팅하는 싱글 사인온 서버인 Authentik으로 위임할 수 있습니다. 이렇게 하면 대시보드를 포함한 모든 경로가 하나의 로그인 절차 뒤에 배치되며, 단일 지점에서 접근 권한을 즉시 취소할 수 있습니다.

Docker 소켓은 root 권한과 같습니다

/var/run/docker.sock는 호스트의 /을 마운트하는 컨테이너를 생성할 수 있는 API입니다. 이 API에 접근하는 것은 해당 머신의 root 권한을 갖는 것과 같으며, Traefik은 레이블을 읽기 위해 이 권한이 필요합니다.

마운트 시 :ro를 유지하되, 이것이 무엇을 의미하는지 명확히 이해해야 합니다. 이는 소켓 파일을 읽기 전용으로 만들 뿐, 소켓을 통해 전달되는 POST 요청을 막지는 못합니다. 진정한 완화책은 Traefik에 직접 소켓을 넘기지 않고, 그 사이에 필터링 프록시를 두는 것입니다.

  dockerproxy:
    image: tecnativa/docker-socket-proxy   # pin the current tag
    restart: unless-stopped
    environment:
      CONTAINERS: 1
      NETWORKS: 1
      POST: 0
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
    networks:
      - proxy

Traefik에서 소켓 볼륨을 제거하고 프로바이더가 프록시를 가리키도록 설정하십시오.

--providers.docker.endpoint=tcp://dockerproxy:2375

Traefik은 컨테이너와 네트워크에 대한 읽기 권한은 유지하면서, 무언가를 생성할 수 있는 능력은 상실하게 됩니다.

방화벽, 포트, 그리고 흔히 잘못 설정하는 규칙

두 개의 포트와 SSH를 개방합니다:

sudo ufw allow OpenSSH
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable

Docker의 게시된 포트는 ufw를 우회합니다. Docker는 자체 iptables 규칙을 삽입하며, 이는 ufw 체인보다 먼저 평가됩니다. 따라서 ports: ["3000:3000"]으로 시작된 컨테이너는 ufw에서 거부(deny) 규칙을 설정했더라도 인터넷에서 접근할 수 있습니다. 이에 대한 방어는 방화벽 설정이 아닌 구조적인 접근이 필요합니다. Traefik을 통해서만 포트를 게시하고, 다른 모든 컨테이너에는 networks: [proxy]만 부여하십시오. 호스트에 반드시 접근해야 하는 서비스가 있다면, "127.0.0.1:3000:3000"을 사용하여 루프백(loopback)에 바인딩하십시오.

문제 해결: 실제로 마주하게 될 오류

404 page not found, Traefik이 반환하는 오류입니다. 일치하는 라우터가 없습니다. 발생 가능성이 높은 순서대로 나열하면 다음과 같습니다. 컨테이너에 traefik.enable=true이 누락되었거나(exposedByDefault=false 설정 필요), Host() 규칙이 입력한 도메인 이름과 일치하지 않거나, 라벨 간의 라우터 이름이 서로 다릅니다(routers.gitea.rule와 routers.gitea.entrypoints는 동일한 단어여야 합니다). 또는 호스트 이름을 작은따옴표로 감쌌을 수 있습니다. Traefik v3는 매처 내부에서 백틱이나 이스케이프된 큰따옴표(\")를 사용해야 하며, 작은따옴표는 거부합니다.

502 Bad Gateway. 라우터는 일치했으나 백엔드에 연결할 수 없는 상태입니다. 대부분의 경우 컨테이너가 proxy 네트워크에 연결되지 않은 것이 원인이므로 docker inspect -f '{{json .NetworkSettings.Networks}}' gitea을 확인하십시오. 다른 원인으로는 잘못된 loadbalancer.server.port 설정이 있습니다. 호스트 포트를 지정했거나 애플리케이션이 다른 포트에서 대기 중일 수 있습니다. 로그에 시도 내용이 기록됩니다: dial tcp 172.18.0.5:8080: connect: connection refused.

브라우저 경고 발생, 인증서가 TRAEFIK DEFAULT CERT로 발급됨. 해당 호스트 이름에 대한 인증서가 존재하지 않아 Traefik이 자체 서명된 기본 인증서를 제공한 것입니다. ACME 관련 로그를 확인하십시오:

unable to obtain ACME certificate for domains "git.example.com" ...
acme: error: 400 ... DNS problem: NXDOMAIN looking up A for git.example.com

DNS가 아직 서버를 가리키고 있지 않습니다. 레코드를 수정하고 TTL이 만료될 때까지 기다린 뒤 Traefik을 재시작하십시오.

Invalid response from http://git.example.com/.well-known/acme-challenge/... (HTTP 챌린지 관련): 외부에서 80번 포트로 Traefik에 접근할 수 없는 상태입니다. 보통 ufw가 아닌 VPS 앞단의 제공자 수준 방화벽이 원인입니다.

인증서가 발급되지 않으며, Cloudflare의 주황색 구름(프록시)이 켜져 있음. Cloudflare가 엣지에서 TLS를 종료하므로 TLS-ALPN-01 챌린지를 완료할 수 없습니다. 인증서 발급 중에는 레코드를 DNS-only로 변경하거나, API 토큰을 사용하여 DNS-01 챌린지로 전환하십시오. 와일드카드 인증서를 발급받으려면 DNS-01 챌린지를 사용해야 합니다.

리다이렉트 루프. Traefik 앞단에서 이미 TLS를 종료하고 평문(plaintext)을 80번 포트로 전달하고 있습니다. 이때 엔트리포인트 리다이렉트 설정이 다시 HTTPS로 요청을 보내면서 루프가 발생합니다. 두 리다이렉트 설정 중 하나를 제거하십시오.

서비스 유지하기

Docker unit은 부팅 시 활성화되어야 하며(systemctl is-enabled docker), restart: unless-stopped를 사용하면 재부팅 후에도 스택이 자동으로 복구됩니다. 명시적인 제어를 원한다면 docker compose -f /srv/edge/compose.yml up -d와 RemainAfterExit=yes을 실행하는 작은 systemd unit을 작성하여 systemctl status edge 및 실행 순서를 제어할 수 있습니다.

Traefik 태그는 고정하고(traefik:v3.7, latest는 절대 사용 금지), 여전히 릴리스를 제공하는 브랜치를 선택하십시오. 2026-10-12 기준으로 Traefik은 v3.7만을 지원되는 v3 브랜치로 명시하고 있습니다. v3.5는 2025년 11월 7일에 마지막 릴리스인 v3.5.6을 배포했으며, 해당 버전의 Docker 프로바이더는 API 버전 1.24로 하드코딩되어 있습니다. Docker Engine 29 이상은 이를 거부하며 client version 1.24 is too old 오류를 발생시키므로 컨테이너 라우팅이 불가능해집니다. Traefik 3.6.1에서 Docker API 버전 협상 기능이 추가되었으며, 이것이 앞선 compose 파일에서 v3.7을 사용하는 이유입니다. v2에서 v3로 업그레이드할 때 규칙 구문과 프로바이더 이름이 변경되었으므로, 무인 latest를 수행하면 더 이상 이해할 수 없는 설정을 그대로 다시 불러올 위험이 있습니다. 업그레이드는 신중하게 진행하십시오. 마이그레이션 노트를 읽고, 태그를 변경하고, docker compose up -d traefik을 수행한 뒤 로그를 모니터링하십시오. 여전히 v2 태그를 사용 중이라면 Traefik v2에서 v3로의 마이그레이션 가이드를 통해 모든 이름 변경 사항, 호환 모드, 그리고 인증서를 유지하며 롤백하는 방법을 확인할 수 있습니다.

./letsencrypt와 각 애플리케이션의 데이터 볼륨을 백업하십시오. Traefik은 compose 파일로부터 재구축할 수 없는 다른 상태 정보를 저장하지 않습니다.

규모 확장 시 발생하는 문제

첫 번째 한계는 처리량이 아니라 단일 서버입니다. VPS 한 대에서 운영하는 Traefik 한 개는 5개 애플리케이션의 단일 장애 지점이 되며, acme.json은 플랫 파일 저장소를 사용하므로 두 개의 Traefik 인스턴스가 쓰기를 시도하면 데이터가 손상됩니다. 규모를 확장하려면 인증서 저장소를 파일 외부로 옮기거나 다른 곳에서 TLS를 종료해야 합니다.

두 번째는 장기 연결입니다. 서버 전송 이벤트(SSE), 대용량 업로드, 느린 클라이언트는 엔트리포인트의 응답 시간 제한에 걸리며, --entryPoints.websecure.transport.respondingTimeouts.readTimeout과 그 형제인 writeTimeout, idleTimeout가 이를 조절하는 설정값입니다. WebSockets는 별도의 추가 설정 없이 통과합니다.

세 번째는 디스크입니다. --accesslog=true는 stdout으로 기록하며, Docker의 json-file 드라이버는 제한을 두지 않으면 이를 영구적으로 보관합니다. Traefik 서비스에 logging.options.max-size를 설정하거나, 접근 로그를 파일로 기록하고 로테이트하십시오.

이 모든 과정에 오케스트레이터는 필요하지 않습니다. 다만 실제 IP를 보유하고 80번 및 443번 포트가 외부로 열려 있는, 직접 제어 가능한 서버가 필요할 뿐입니다. 작은 VPS 한 대가 전체 의존성 목록의 전부입니다.

FAQ

Traefik을 사용해도 certbot이 여전히 필요한가요?

아니요. Traefik의 ACME 리졸버는 라우팅하는 모든 호스트네임에 대해 인증서를 요청하고 갱신하며, 이를 acme.json에 저장합니다. Certbot은 nginx나 다른 서버가 직접 TLS를 종료할 때 사용하는 적절한 도구입니다. 동일한 호스트네임에 두 도구를 모두 실행하면 Let's Encrypt의 속도 제한(rate limit)만 소진하게 됩니다.

컨테이너가 Traefik을 통해 404 오류를 반환하는 이유는 무엇인가요?

Traefik이 반환하는 404는 요청과 일치하는 라우터가 전혀 없음을 의미합니다. 컨테이너에 traefik.enable=true이 포함되어 있는지(exposedByDefault=false이 설정된 경우 필수), Host() 값이 입력한 이름과 일치하는지, 그리고 해당 애플리케이션의 모든 레이블에서 라우터 이름이 동일한지 확인하십시오. Traefik v3는 매처 내부에서 백틱이나 이스케이프된 큰따옴표를 사용해야 하며, 작은따옴표는 거부됩니다.

여기서 404와 502의 차이점은 무엇인가요?

404는 라우팅이 전혀 이루어지지 않았음을 의미하며, 502는 라우터는 일치했으나 백엔드가 연결을 거부했음을 의미합니다. 502 오류의 일반적인 원인은 컨테이너가 proxy 네트워크에 연결되지 않았거나, loadbalancer.server.port이 컨테이너 내부에서 앱이 수신 대기하는 포트가 아닌 외부로 노출된 포트를 가리키고 있는 경우입니다. 접근 로그에는 Traefik이 연결을 시도한 정확한 주소가 기록됩니다.

Docker 소켓을 읽기 전용으로 마운트하는 것만으로 충분한가요?

:ro 플래그는 소켓 파일을 읽기 전용으로 만들 뿐 그 뒤의 API까지 제한하지는 않습니다. POST 요청은 여전히 소켓을 통해 전달되며, Docker API에 대한 접근 권한은 호스트의 root 권한과 동일합니다. 더 강력한 구성은 위에서 설명한 docker-socket-proxy 컨테이너를 사용하는 것이며, 이는 Traefik에 컨테이너 및 네트워크 읽기 권한만 노출하고 쓰기 작업은 완전히 차단합니다.

Traefik으로 와일드카드 인증서를 발급할 수 있나요?

DNS 제공업체의 API 토큰을 사용하는 DNS-01 챌린지를 통해서만 가능합니다. TLS-ALPN-01과 HTTP-01은 각각 단일 호스트네임만 검증하므로 와일드카드 인증서를 생성할 수 없습니다. 또한 Cloudflare와 같은 CDN이 VPS 앞단에서 TLS를 종료하여 다른 두 챌린지가 완료되지 않는 경우에도 DNS-01이 해결책입니다.