본문 바로가기
서버, 인프라

[월 1만원으로 내 서비스 올리기] 도메인 연결하고 HTTPS까지 적용해봤습니다 — Hetzner 실전 세팅 7편

by 요즘IT 2026. 5. 27.

Hetzner 서버에 도메인 연결하고 Certbot으로 HTTPS SSL 인증서 발급까지 Docker Compose 환경에서 실제 삽질 기반으로 정리했어요.

이전글 : [서버, 인프라] - 서버에 내 도메인 달기 — 도메인 구매부터 DNS 연결까지 (6편)


5편에서 Jenkins CI/CD 파이프라인까지 완성했잖아요.

근데 아직 http://서버IP 로 접속하는 거라 좀 아쉽거든요. 도메인 연결하고 HTTPS까지 붙여야 진짜 서비스처럼 보이죠.

이번 편에서 도메인 연결이랑 SSL 인증서 발급까지 다 끝낼게요. 근데 Docker Compose 환경에서 Certbot 쓰는 게 생각보다 설정할 게 좀 있거든요. 그 부분도 다 정리해뒀어요.


전체 순서 먼저 잡고 갈게요

도메인 DNS 설정 (A 레코드 → 서버 IP)
→ Certbot 컨테이너 추가
→ SSL 인증서 발급
→ Nginx HTTPS 설정
→ HTTP → HTTPS 리다이렉트
→ Hetzner 방화벽 443 포트 오픈

1단계 — 도메인 DNS 설정

도메인 등록 업체(가비아, 호스팅케이알 등) 콘솔에서 A 레코드 추가하면 돼요.

  • 호스트: @ (루트 도메인)
  • 타입: A
  • 값: 서버 IP
  • TTL: 3600 (기본값)

서브도메인도 쓸 거면 추가로 등록해요. 저는 Jenkins용 서브도메인도 따로 잡았어요.

  • 호스트: jenkins
  • 타입: A
  • 값: 서버 IP (동일)

DNS 전파는 보통 5~10분 걸려요. 아래 명령어로 확인해보면 돼요.

curl -I http://도메인주소

200 OK 나오면 도메인이 서버로 잘 연결된 거예요.


2단계 — Certbot 컨테이너 추가

여기서 잠깐. Docker Compose로 Nginx를 띄우고 있으면 Certbot을 서버에 직접 설치하면 안 돼요.

Certbot이 80포트를 직접 쓰려고 하는데 Nginx 컨테이너가 이미 80포트를 점유하고 있거든요. 충돌 나요.

그래서 Certbot도 컨테이너로 올리는 방식으로 가요.

인증서 저장 디렉토리 먼저 만들게요.

mkdir -p /opt/app/certbot/conf
mkdir -p /opt/app/certbot/www

docker-compose.yml에 Certbot 추가하고 Nginx에 443 포트랑 볼륨 마운트도 추가해요.

services:
  nginx:
    image: nginx:alpine
    container_name: nginx
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx/default.conf:/etc/nginx/conf.d/default.conf
      - ./certbot/conf:/etc/letsencrypt
      - ./certbot/www:/var/www/certbot
    depends_on:
      - front-app
      - api-app

  certbot:
    image: certbot/certbot
    container_name: certbot
    volumes:
      - ./certbot/conf:/etc/letsencrypt
      - ./certbot/www:/var/www/certbot

  front-app:
    image: front-app
    container_name: front-app
    restart: unless-stopped
    ports:
      - "10000:10000"

  api-app:
    image: api-app
    container_name: api-app
    restart: unless-stopped
    ports:
      - "10081:10081"

3단계 — Nginx에 Certbot 인증 경로 추가

인증서 발급 전에 Nginx 설정에 .well-known/acme-challenge/ 경로를 추가해줘야 해요. Certbot이 도메인 소유권 확인할 때 이 경로를 쓰거든요.

server {
    listen 80;
    server_name 도메인주소;

    location /.well-known/acme-challenge/ {
        root /var/www/certbot;
    }

    location / {
        proxy_pass http://front-app:10000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }

    location /api/ {
        proxy_pass http://api-app:10081;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}

서브도메인(Jenkins 등)도 쓴다면 별도 server 블록 추가해요.

server {
    listen 80;
    server_name jenkins.도메인주소;

    location /.well-known/acme-challenge/ {
        root /var/www/certbot;
    }

    location / {
        proxy_pass http://서버IP:8090;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

설정 적용하고 재시작해요.

docker compose -f /opt/app/docker-compose.yml down
docker compose -f /opt/app/docker-compose.yml up -d

Certbot Let's Encrypt SSL certificate issued
Certbot Let's Encrypt SSL certificate issued


4단계 — SSL 인증서 발급

여러 도메인을 한 번에 발급할 수 있어요. -d 옵션으로 추가하면 돼요.

docker compose -f /opt/app/docker-compose.yml run --rm certbot certonly --webroot \
  -w /var/www/certbot \
  -d 도메인주소 \
  -d jenkins.도메인주소 \
  --email 본인이메일@gmail.com \
  --agree-tos \
  --no-eff-email

Successfully received certificate 나오면 발급 완료예요.


5단계 — Nginx HTTPS 설정

인증서 발급됐으면 Nginx에 443 설정 추가하고 HTTP → HTTPS 리다이렉트 잡아줄게요.

server {
    listen 80;
    server_name 도메인주소;

    location /.well-known/acme-challenge/ {
        root /var/www/certbot;
    }

    location / {
        return 301 https://$host$request_uri;
    }
}

server {
    listen 443 ssl;
    server_name 도메인주소;

    ssl_certificate /etc/letsencrypt/live/도메인주소/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/도메인주소/privkey.pem;

    location / {
        proxy_pass http://front-app:10000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }

    location /api/ {
        proxy_pass http://api-app:10081;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

server {
    listen 80;
    server_name jenkins.도메인주소;

    location /.well-known/acme-challenge/ {
        root /var/www/certbot;
    }

    location / {
        return 301 https://$host$request_uri;
    }
}

server {
    listen 443 ssl;
    server_name jenkins.도메인주소;

    ssl_certificate /etc/letsencrypt/live/도메인주소/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/도메인주소/privkey.pem;

    location / {
        proxy_pass http://서버IP:8090;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

Nginx 재시작해요.

docker compose -f /opt/app/docker-compose.yml restart nginx

6단계 — Hetzner 방화벽 443 포트 오픈

여기서 잠깐. 이거 빠뜨리면 curl: (28) Failed to connect to 도메인주소 port 443 이 나와요.

Nginx 설정 다 잘 됐는데 접속이 안 되면 방화벽 문제예요. 저도 이것 때문에 잠깐 헤맸거든요.

Hetzner 콘솔 → Firewalls → 해당 방화벽 → Rules → Add Rule

  • Direction: Inbound
  • Protocol: TCP
  • Port: 443
  • Source IP: 0.0.0.0/0

저장하면 바로 적용돼요.

Hetzner firewall rules inbound 443 HTTPS
Hetzner firewall rules inbound 443 HTTPS


7단계 — 인증서 자동 갱신 설정

Let's Encrypt 인증서는 90일마다 만료돼요. 자동 갱신 설정해두지 않으면 90일 후에 HTTPS가 갑자기 안 돼요.

크론탭으로 자동 갱신 등록할게요.

crontab -e

아래 내용 추가해요.

0 3 * * * docker compose -f /opt/app/docker-compose.yml run --rm certbot renew && docker compose -f /opt/app/docker-compose.yml restart nginx

매일 새벽 3시에 인증서 갱신 체크하고 만료 30일 전이면 자동으로 갱신해줘요.


삽질 포인트 정리

1. Docker Compose 환경에서 Certbot 직접 설치 금지

Nginx 컨테이너가 80포트 점유 중이라 충돌 나요. Certbot도 컨테이너로 올려야 해요.

2. .well-known/acme-challenge/ 경로 필수

인증서 발급 전에 Nginx 설정에 이 경로 없으면 도메인 소유권 확인 실패해요.

3. Hetzner 방화벽 443 포트

Nginx 설정 다 맞아도 방화벽에서 443 안 열면 접속 안 돼요. 꼭 확인해야 해요.

4. 인증서 자동 갱신

90일 만료니까 크론탭 설정 꼭 해두세요. 안 하면 나중에 갑자기 HTTPS 죽어요.


최종 확인

curl -I https://도메인주소

HTTP/2 200 나오면 HTTPS 완전히 적용된 거예요.

결국 핵심은 세 가지예요. Certbot도 컨테이너로 올리고, Nginx에 acme-challenge 경로 추가하고, Hetzner 방화벽 443 포트 열어주는 것. 이 세 가지만 빠뜨리지 않으면 돼요.

다음 편에서는 OAuth2 소셜 로그인 (카카오, 네이버, Google, Facebook, GitHub) 리다이렉트 URI 업데이트 방법 다뤄볼게요.