화. 8월 12th, 2025

G: 안녕하세요! 🚀 오늘은 클라우드 기반의 강력한 오픈소스 백엔드 플랫폼, Supabase를 직접 서버에 구축하고 (Self-Host), 나아가 나만의 커스텀 도메인으로 API를 제공하며, 안전한 HTTPS 연결까지 완벽하게 설정하는 방법에 대해 자세히 알아보겠습니다. 프로덕션 환경에 적합한 안정적인 설정을 목표로 합니다! 🔒


💡 왜 Supabase를 Self-Host 해야 할까요?

Supabase는 기본적으로 클라우드 서비스로 제공되지만, 특정 상황에서는 직접 호스팅하는 것이 유리할 수 있습니다:

  • 데이터 주권 및 보안 강화: 민감한 데이터를 직접 통제하고 싶을 때. 🛡️
  • 비용 최적화: 특정 워크로드에서는 클라우드 서비스보다 직접 운영이 저렴할 수 있습니다. 💰
  • 커스터마이징 및 유연성: 서비스의 모든 부분을 직접 제어하고 미세 조정하고 싶을 때. ⚙️
  • 내부 네트워크 통합: 기존 온프레미스 인프라와 긴밀하게 통합해야 할 때. 🔗

이 가이드는 이러한 필요성을 가진 분들을 위해 Supabase를 성공적으로 프로덕션 환경에 배포하는 로드맵을 제시합니다.


🛠️ 시작하기 전 준비물

본격적인 설정에 앞서 필요한 것들을 확인해봅시다.

  1. 서버 (VM/VPS 또는 물리 서버):
    • 운영체제: Ubuntu 20.04+ 또는 다른 Linux 배포판 (Debian, CentOS 등).
    • 사양: 최소 4GB RAM, 2vCPU (권장: 8GB RAM, 4vCPU 이상). 스토리지 용량은 데이터에 따라 충분히 확보해야 합니다.
    • 퍼블릭 IP 주소: 외부에서 접근 가능한 고정 IP가 필요합니다.
  2. 도메인 이름:
    • api.yourdomain.com, auth.yourdomain.com, storage.yourdomain.com 등 Supabase 서비스에 사용할 커스텀 도메인 (또는 서브도메인)이 필요합니다.
    • DNS 레코드를 설정할 수 있어야 합니다.
  3. Docker 및 Docker Compose:
  4. 기본적인 Linux CLI 지식:
    • 파일 시스템 탐색, 편집기 사용 (vi/nano), 명령어 실행 등에 익숙해야 합니다.
  5. SSL/TLS 인증서 발급 준비:
    • Let’s Encrypt를 사용할 예정이므로, Certbot 또는 Caddy와 같은 도구가 필요합니다.

📝 1단계: Supabase Docker Compose 기본 설정

Supabase는 모든 핵심 서비스를 Docker 컨테이너로 제공합니다. supabase/supabase GitHub 저장소를 통해 쉽게 배포할 수 있습니다.

1.1 Supabase 저장소 클론

SSH로 서버에 접속한 후, 원하는 디렉토리에 Supabase 저장소를 클론합니다.

cd /opt # 또는 원하는 경로
git clone --depth 1 https://github.com/supabase/supabase
cd supabase

1.2 .env 파일 설정

Supabase의 핵심 설정은 .env 파일에 정의됩니다. supabase 디렉토리 내에 있는 .env.example 파일을 복사하여 .env 파일을 생성합니다.

cp .env.example .env

이제 nano 또는 vi 에디터로 .env 파일을 열어 필수 설정들을 변경해야 합니다.

nano .env

반드시 변경해야 할 주요 설정:

  • JWT_SECRET: 매우 중요합니다! 길이가 길고 복잡한 무작위 문자열로 변경하세요. Supabase 서비스들이 인증에 사용하는 비밀 키입니다.
    • 생성 예시: openssl rand -hex 32 또는 온라인 JWT Secret Generator.
    • JWT_SECRET="YOUR_VERY_STRONG_JWT_SECRET_HERE_AT_LEAST_32_CHARS"
  • POSTGRES_PASSWORD: PostgreSQL 데이터베이스의 비밀번호를 강력하게 설정하세요.
    • POSTGRES_PASSWORD="YOUR_SUPER_SECURE_PG_PASSWORD"
  • ANON_KEY / SERVICE_ROLE_KEY: 초기 배포 시에는 .env.example에 있는 플레이스홀더 값을 그대로 둡니다. Supabase가 실행된 후 대시보드에서 실제 키를 가져와 업데이트할 것입니다.
    • 이 두 키는 나중에 사용자의 API 요청을 인증하는 데 사용됩니다.
  • DASHBOARD_PASSWORD: Supabase Studio (대시보드) 접근 비밀번호입니다.
    • DASHBOARD_PASSWORD="YOUR_DASHBOARD_PASSWORD"

다른 권장 설정:

  • AUTH_EXTERNAL_URL: 인증 서비스 (GoTrue)의 외부 접근 URL입니다. 아직 커스텀 도메인을 설정하기 전이므로, 초기에는 http://localhost:8000 (또는 서버의 임시 IP 주소)로 두세요. 나중에 HTTPS 도메인으로 업데이트할 것입니다.
    • AUTH_EXTERNAL_URL="http://your_server_ip:8000"
  • SITE_URL: Supabase Studio가 참조하는 기본 URL입니다. AUTH_EXTERNAL_URL과 유사하게 설정합니다.
    • SITE_URL="http://your_server_ip:8000"

.env 파일 편집 후 저장하고 닫습니다.

1.3 Supabase 서비스 시작 (초기)

이제 Docker Compose를 사용하여 Supabase 서비스를 시작합니다.

docker compose up -d

이 명령어는 백그라운드에서 Supabase의 모든 필수 컨테이너 (PostgreSQL, GoTrue, Storage, Realtime, Kong, Inbucket 등)를 시작합니다.

컨테이너가 정상적으로 실행되는지 확인하세요.

docker compose ps

모든 컨테이너의 상태가 Up으로 표시되어야 합니다.

1.4 Supabase Studio 접속 및 초기 확인

웹 브라우저에서 `http://

:8000`으로 접속하여 Supabase Studio에 접근할 수 있는지 확인합니다. `.env` 파일에 설정한 `DASHBOARD_PASSWORD`로 로그인할 수 있습니다. 로그인 후 `Settings` -> `API` 페이지로 이동하여 `Project API keys` 섹션에서 `anon public` 키와 `service role` 키를 복사해둡니다. 이 키들은 3단계에서 `.env` 파일에 업데이트해야 합니다. — ### 🌐 2단계: 커스텀 도메인 DNS 설정 Supabase의 각 서비스 (인증, 스토리지, API 게이트웨이 등)를 커스텀 도메인으로 접근할 수 있도록 DNS 레코드를 설정해야 합니다. **핵심 아이디어:** 하나의 메인 도메인(`api.yourdomain.com`)을 Supabase 서버의 IP 주소로 연결하고, Nginx 또는 Caddy와 같은 리버스 프록시가 이 도메인을 통해 들어오는 요청을 Supabase의 내부 서비스로 라우팅하는 방식입니다. 예시로 `supabase.yourdomain.com`을 메인 도메인으로 사용할 경우: | 레코드 타입 | 이름/호스트 | 값 | | :———- | :———- | :—————— | | `A` | `supabase` | `YOUR_SERVER_IP` | 만약 각 서비스를 별도의 서브도메인으로 명시적으로 분리하고 싶다면: | 레코드 타입 | 이름/호스트 | 값 | | :———- | :———- | :—————— | | `A` | `api` | `YOUR_SERVER_IP` | | `A` | `auth` | `YOUR_SERVER_IP` | | `A` | `storage` | `YOUR_SERVER_IP` | | `A` | `functions` | `YOUR_SERVER_IP` | **⚠️ 중요:** DNS 변경 사항이 전 세계에 전파되는 데는 몇 분에서 몇 시간이 걸릴 수 있습니다 (DNS Propagation). `nslookup` 또는 `dig` 명령어를 사용하여 설정한 도메인이 올바른 IP 주소로 확인되는지 확인하세요. “`bash dig A api.yourdomain.com “` — ### 📝 3단계: Supabase `.env` 파일 업데이트 (커스텀 도메인 & API 키) 이제 커스텀 도메인을 사용하도록 Supabase 설정을 업데이트하고, 1단계에서 복사해둔 API 키를 `.env` 파일에 반영합니다. “`bash nano .env “` **수정할 주요 설정:** * **`SUPABASE_PUBLIC_URL`**: Supabase의 모든 서비스가 외부로 노출될 기본 URL입니다. * `SUPABASE_PUBLIC_URL=”https://api.yourdomain.com”` (나중에 HTTPS 적용 후) * 현재는 `http://api.yourdomain.com`으로 설정하고 4단계에서 HTTPS로 변경합니다. * **`ANON_KEY`**: 1단계에서 Supabase Studio에서 복사한 `anon public` 키를 붙여넣습니다. * `ANON_KEY=”YOUR_COPIED_ANON_PUBLIC_KEY”` * **`SERVICE_ROLE_KEY`**: 1단계에서 Supabase Studio에서 복사한 `service role` 키를 붙여넣습니다. * `SERVICE_ROLE_KEY=”YOUR_COPIED_SERVICE_ROLE_KEY”` * **서비스별 URL (선택적이지만 권장):** Supabase는 내부적으로 Kong API Gateway를 사용하여 라우팅하므로 `SUPABASE_PUBLIC_URL`만 설정해도 대부분 작동하지만, 각 서비스의 명시적인 URL을 설정하면 좋습니다. * `AUTH_EXTERNAL_URL`: `https://auth.yourdomain.com` (또는 `https://api.yourdomain.com/auth/v1`) * `STORAGE_URL`: `https://storage.yourdomain.com` (또는 `https://api.yourdomain.com/storage/v1`) * `FUNCTIONS_URL`: `https://functions.yourdomain.com` (또는 `https://api.yourdomain.com/functions/v1`) * `REALTIME_URL`: `https://realtime.yourdomain.com` (또는 `https://api.yourdomain.com/realtime/v1`) * `INBUCKET_URL`: `https://inbucket.yourdomain.com` (메일 테스트용, 선택사항) **팁:** Kong을 통해 하나의 `api.yourdomain.com`으로 모든 서비스를 프록시할 계획이라면, `SUPABASE_PUBLIC_URL`을 메인 도메인으로 설정하고, 다른 `*_URL` 변수들은 `SUPABASE_PUBLIC_URL`을 기반으로 경로만 변경해주면 됩니다. 예: `AUTH_EXTERNAL_URL=”https://api.yourdomain.com/auth/v1″` 설정 완료 후 `.env` 파일을 저장합니다. #### Supabase 재시작 변경된 `.env` 설정을 적용하려면 Supabase 컨테이너를 재시작해야 합니다. “`bash docker compose down docker compose up -d “` — ### 🔒 4단계: HTTPS (SSL/TLS) 설정 – Caddy 리버스 프록시 활용 프로덕션 환경에서 HTTPS는 필수입니다. 여기서는 자동 HTTPS 기능을 제공하는 **Caddy**를 사용하여 Let’s Encrypt 인증서를 자동으로 발급받고 갱신하며, Supabase 컨테이너로 요청을 프록시하는 방법을 안내합니다. Nginx보다 설정이 훨씬 간편합니다. #### 4.1 Caddy용 `docker-compose.yml` 수정 Supabase가 기본으로 제공하는 `docker-compose.yml` 파일에 Caddy 서비스를 추가하고, Kong (Supabase의 API 게이트웨이)이 Caddy를 통해서만 접근되도록 수정합니다. `supabase` 디렉토리 내의 `docker-compose.yml` 파일을 엽니다. “`bash nano docker-compose.yml “` **수정 내용:** 1. **`kong` 서비스 수정:** * `ports` 섹션에서 `8000:8000`을 제거합니다. 이제 Kong은 외부로 직접 노출되지 않고, Caddy 내부 네트워크를 통해 접근됩니다. 2. **`caddy` 서비스 추가:** * `services` 섹션에 `caddy` 서비스를 추가합니다. 수정된 `docker-compose.yml` 예시 (관련 부분만): “`yaml version: ‘3.7’ services: # … (다른 서비스들은 그대로 둡니다) kong: image: kong:2.8.1-alpine # … (생략) # ports: # – 8000:8000 # 이 줄을 제거하거나 주석 처리 restart: always environment: # … (환경 변수들은 그대로 둡니다) KONG_DATABASE: postgres KONG_PG_HOST: db KONG_PG_PORT: 5432 KONG_PG_USER: kong KONG_PG_PASSWORD: ${POSTGRES_PASSWORD} KONG_ANONYMOUS_REPORTS: “off” KONG_PROXY_LISTEN: “0.0.0.0:8000” # Caddy가 내부적으로 이 포트로 연결 KONG_ADMIN_LISTEN: “0.0.0.0:8001” KONG_ADMIN_ACCESS_LOG: /dev/stdout KONG_ADMIN_ERROR_LOG: /dev/stderr KONG_PROXY_ACCESS_LOG: /dev/stdout KONG_PROXY_ERROR_LOG: /dev/stderr depends_on: – db – kong-migrations networks: – default # 기존 네트워크 사용 caddy: image: caddy/caddy:latest restart: unless-stopped ports: – “80:80” # HTTP (Let’s Encrypt 챌린지 및 자동 리디렉션) – “443:443″ # HTTPS volumes: – ./Caddyfile:/etc/caddy/Caddyfile # Caddy 설정 파일 마운트 – caddy_data:/data # Caddy 데이터 (인증서 저장) networks: – default # 기존 네트워크 사용 depends_on: – kong # Kong 서비스가 시작된 후 Caddy가 시작되도록 종속성 설정 # … (다른 서비스들은 그대로 둡니다) volumes: # … (기존 볼륨들은 그대로 둡니다) caddy_data: {} # Caddy 데이터를 위한 새 볼륨 추가 “` `volumes` 섹션에 `caddy_data: {}`를 추가하는 것을 잊지 마세요. #### 4.2 Caddyfile 생성 `supabase` 디렉토리 내에 `Caddyfile`이라는 이름의 파일을 새로 생성하고 다음과 같이 작성합니다. “`bash nano Caddyfile “` “`caddy # 단일 도메인으로 모든 서비스 프록시 (권장) https://api.yourdomain.com { reverse_proxy kong:8000 # Kong 서비스로 프록시 } # 또는 각 서비스를 별도의 서브도메인으로 프록시 (더 복잡) # https://api.yourdomain.com { # reverse_proxy kong:8000 # } # https://auth.yourdomain.com { # reverse_proxy kong:8000 # } # https://storage.yourdomain.com { # reverse_proxy kong:8000 # } # … 다른 서비스들도 필요에 따라 추가 “` `api.yourdomain.com` 부분을 **실제 사용하려는 도메인**으로 변경하세요. 하나의 도메인으로 Kong을 통해 모든 Supabase 서비스를 프록시하는 것이 일반적이고 간단합니다. #### 4.3 방화벽 설정 서버의 방화벽 (UFW 또는 Cloud Provider Security Group)에서 `80` (HTTP) 및 `443` (HTTPS) 포트가 외부에서 접근 가능하도록 허용해야 합니다. **UFW 예시:** “`bash sudo ufw allow 80/tcp sudo ufw allow 443/tcp sudo ufw enable # UFW 활성화 (아직 활성화하지 않았다면) sudo ufw status # 상태 확인 “` #### 4.4 Supabase 서비스 재시작 (Caddy 포함) 모든 변경 사항을 적용하기 위해 기존 Supabase 컨테이너를 중지하고 다시 시작합니다. “`bash docker compose down docker compose up -d “` Caddy 컨테이너가 시작되면 자동으로 Let’s Encrypt와 통신하여 SSL 인증서를 발급받고 HTTPS 트래픽을 처리하기 시작합니다. #### 4.5 `.env` 파일 HTTPS URL로 최종 업데이트 Caddy 설정이 완료되고 HTTPS 접속이 가능해지면, `.env` 파일의 `SUPABASE_PUBLIC_URL` 및 기타 서비스 URL들을 `https://`로 시작하도록 최종 업데이트합니다. “`bash nano .env “` “` SUPABASE_PUBLIC_URL=”https://api.yourdomain.com” ANON_KEY=”YOUR_COPIED_ANON_PUBLIC_KEY” SERVICE_ROLE_KEY=”YOUR_COPIED_SERVICE_ROLE_KEY” # 다른 서비스 URL들도 https로 변경 AUTH_EXTERNAL_URL=”https://api.yourdomain.com/auth/v1″ STORAGE_URL=”https://api.yourdomain.com/storage/v1″ FUNCTIONS_URL=”https://api.yourdomain.com/functions/v1″ REALTIME_URL=”https://api.yourdomain.com/realtime/v1″ INBUCKET_URL=”https://api.yourdomain.com/inbucket/v1″ # Inbucket을 사용하는 경우 “` 변경 후 다시 한번 Supabase 서비스를 재시작합니다. “`bash docker compose down docker compose up -d “` — ### ✅ 5단계: 설정 확인 및 테스트 이제 모든 설정이 완료되었습니다. 웹 브라우저와 간단한 코드를 통해 제대로 작동하는지 확인해봅시다. #### 5.1 웹 브라우저에서 Supabase Studio 접속 `https://api.yourdomain.com` (또는 `https://your_studio_domain.com` – 만약 Caddyfile에서 별도로 Studio 도메인을 설정했다면) 으로 접속하여 Supabase Studio가 HTTPS로 정상적으로 표시되는지 확인합니다. 브라우저의 자물쇠 아이콘 🔒을 눌러 인증서 정보도 확인해보세요. #### 5.2 API 엔드포인트 테스트 (cURL) 터미널에서 cURL 명령어를 사용하여 API 게이트웨이에 접근해봅니다. “`bash curl -v “https://api.yourdomain.com/rest/v1/” \ -H “apikey: YOUR_ANON_KEY” \ -H “Authorization: Bearer YOUR_ANON_KEY” “` 성공적으로 연결되면 `200 OK` 응답과 함께 Supabase API의 정보가 JSON 형태로 반환될 것입니다. #### 5.3 클라이언트 코드 테스트 (JavaScript 예시) 간단한 HTML/JavaScript 파일을 생성하여 Supabase 클라이언트 라이브러리로 연결을 테스트합니다. `test.html` 파일: “`html Supabase Self-Host Test

Supabase Self-Host Test

“` 이 파일을 웹 서버에 올려서 (또는 로컬에서 직접 열어서) 콘솔과 화면에 결과가 정상적으로 표시되는지 확인합니다. — ### 🚨 6단계: 트러블슈팅 팁 문제가 발생하면 다음 사항들을 확인해보세요. * **방화벽 (Firewall):** 80, 443 포트가 열려있는지 다시 확인합니다. 클라우드 환경에서는 보안 그룹 설정도 확인하세요. * **DNS 전파 (Propagation):** 도메인 설정 변경이 완전히 적용되었는지 `dig` 또는 `nslookup`으로 확인합니다. * **Docker 컨테이너 상태:** `docker compose ps`로 모든 컨테이너가 `Up` 상태인지 확인하고, `docker compose logs ` 명령어로 특정 서비스의 로그를 확인하여 에러 메시지를 찾습니다. (예: `docker compose logs caddy`, `docker compose logs kong`) * **`.env` 파일 오타:** 환경 변수 이름이나 값에 오타가 없는지 꼼꼼히 확인합니다. 특히 `JWT_SECRET`과 API 키는 정확해야 합니다. * **볼륨 마운트:** `docker-compose.yml`에서 Caddyfile과 Caddy 데이터 볼륨이 올바르게 마운트되었는지 확인합니다. * **시간 동기화:** 서버 시간이 정확한지 확인합니다. Let’s Encrypt는 시간 동기화에 민감할 수 있습니다. * **Supabase Community:** Supabase GitHub Discussions 또는 Discord 채널에서 유사한 문제에 대한 해결책을 찾을 수 있습니다. — ### 🚀 7단계: 프로덕션 환경 고려사항 성공적인 배포를 넘어, 안정적인 프로덕션 운영을 위한 추가 고려사항입니다. * **데이터베이스 백업:** PostgreSQL 데이터베이스는 Supabase의 핵심입니다. 정기적인 백업 전략을 수립하고 자동화해야 합니다. `pg_dump`를 사용하여 스케줄링된 백업을 설정하세요. * **모니터링:** 서버 리소스 (CPU, 메모리, 디스크), Docker 컨테이너 상태, Supabase 서비스 로그 등을 지속적으로 모니터링해야 합니다. Prometheus, Grafana, ELK 스택 등을 고려할 수 있습니다. * **보안 강화:** * 모든 비밀번호와 API 키를 강력하고 복잡하게 설정합니다. * SSH 키 기반 인증을 사용하고, 비밀번호 인증을 비활성화합니다. * 필요한 포트만 열고, 불필요한 서비스는 비활성화합니다. * 정기적으로 운영체제 및 Docker 이미지를 업데이트하여 보안 취약점을 패치합니다. * **업데이트:** Supabase는 활발히 개발되고 있으므로, 정기적으로 `supabase/supabase` 저장소를 `git pull`하고 `docker compose build –no-cache` 후 `docker compose up -d`를 통해 업데이트하는 것을 고려하세요. (업데이트 전에는 백업 필수!) * **로깅:** 모든 서비스의 로그를 중앙 집중화된 로깅 시스템으로 전송하여 문제 발생 시 빠르게 진단할 수 있도록 합니다. * **스케일링 (추가):** 초기에는 단일 서버로 충분하지만, 트래픽이 증가하면 DB, GoTrue, Storage 등을 별도의 서버로 분리하거나 클러스터링을 고려해야 합니다. — ### 🎉 마치며 이 가이드를 통해 Supabase를 프로덕션 환경에 성공적으로 Self-Host하고, 커스텀 도메인 및 HTTPS까지 완벽하게 설정하는 방법을 익히셨기를 바랍니다. 직접 인프라를 구축하는 것은 복잡할 수 있지만, 그만큼 더 큰 제어권과 유연성을 얻을 수 있습니다. 이제 여러분만의 강력하고 안전한 Supabase 백엔드를 구축하셨으니, 멋진 애플리케이션 개발에 집중하세요! 궁금한 점이 있다면 언제든지 Supabase 커뮤니티나 관련 자료를 찾아보세요. 행복한 코딩 되세요! 😊

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다