TELEPASI

검색하기 전에 통하다

Ubuntu 서버에 SvelteKit + Self-hosted Supabase 구축 가이드

강병우
2026.07.19 👁️ 116

Ubuntu Server에 SvelteKit 웹 애플리케이션과 Self-hosted Supabase를 구축하는 전체 과정을 단계별로 정리한 가이드입니다.
(telepasi.com을 설정하면서 작업했던 내용들을 정리한 문서이니 도움이 되시길 바랍니다. - 게시물로 작성하기 위해 일부 이름이나 path가 변경되었습니다.)

대상 독자: 서버 구축 경험이 없거나 처음 도전하는 개발자
목표: 프로덕션 수준의 HTTPS 웹 서비스 완성


1. 최종 구성 개요

외부에서 접근하는 주소

https://example.com
└─ SvelteKit 메인 웹사이트

https://www.example.com
└─ SvelteKit 메인 웹사이트 (www 별칭)

https://api.example.com
└─ Self-hosted Supabase API 및 Studio

요청 처리 흐름

사용자 브라우저
      │
      │ HTTPS :443
      ▼
호스트 Nginx (리버스 프록시)
      ├─ example.com
      │    └─ 127.0.0.1:3000
      │         └─ PM2 → SvelteKit Node.js
      │
      └─ api.example.com
           └─ 127.0.0.1:8000
                └─ Docker Kong
                     ├─ Auth
                     ├─ PostgREST
                     ├─ Storage
                     ├─ Realtime
                     └─ 기타 Supabase 서비스

주요 디렉터리 구조

/root/supabase/docker/
├─ docker-compose.yml           # Supabase 컨테이너 구성
└─ .env                         # Supabase Backend 환경변수

/var/www/example.com/
├─ current/                     # SvelteKit 배포 파일
├─ logs/                        # 애플리케이션 로그
└─ backups/                     # DB 백업 파일

/home/webuser/
├─ .nvm/                        # Node.js 버전 관리
└─ .pm2/                        # PM2 프로세스 관리

/etc/nginx/sites-available/
├─ example.com                  # 메인 웹 설정
└─ api.example.com              # Supabase API 설정

2. 전체 작업 순서

1. OS 기본 설정 및 업데이트
   ↓
2. SSH 보안 강화
   ↓
3. 서비스 사용자 및 디렉터리 생성
   ├────────────────────────────┐
   ↓                            ↓
4. Docker 설치              9. Node.js/PM2 설치
   ↓                            ↓
5. Supabase 설치            10. Frontend 빌드·배포
   ↓                            ↓
6. Supabase URL 설정           │
   ↓                            │
7. 데이터 마이그레이션          │
   │                            │
   └──────────┬─────────────────┘
              ↓
8. DNS 및 Nginx HTTPS 구성
              ↓
11. OAuth 연동 (선택)
              ↓
12. 백업 자동화
              ↓
13. 모니터링 및 유지보수

3. 1단계 — OS 기본 설정

패키지 업데이트

sudo apt update
sudo apt upgrade -y

필수 도구 설치

sudo apt install -y \
  ca-certificates \
  curl \
  gnupg \
  lsb-release \
  git \
  wget \
  rsync \
  jq \
  software-properties-common \
  apt-transport-https

시간대 설정 (한국 기준)

sudo timedatectl set-timezone Asia/Seoul
timedatectl

시스템 확인

# OS 버전
lsb_release -a

# 디스크 용량
df -h

# 메모리
free -h

권장 최소 사양:

  • Ubuntu 22.04 LTS 이상
  • RAM 2GB 이상 (Supabase는 메모리 사용량이 높음)
  • 디스크 20GB 이상

4. 2단계 — SSH 보안 강화

커스텀 SSH 포트 설정

sudo nano /etc/ssh/sshd_config

다음 설정 변경:

Port 2222
PermitRootLogin no
PasswordAuthentication no  # SSH Key 사용 시

SSH Key 생성 (로컬 PC에서)

# 로컬 PC에서 실행
ssh-keygen -t ed25519 -C "your-email@example.com"

# 공개키를 서버로 복사
ssh-copy-id -p 2222 webuser@<SERVER_IP>

방화벽 설정 (UFW)

# SSH, HTTP, HTTPS 허용
sudo ufw allow 2222/tcp
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp

# 방화벽 활성화 (SSH 접속 확인 후!)
sudo ufw enable
sudo ufw status

⚠️ 주의: 방화벽 활성화 전에 SSH 포트가 허용되었는지 반드시 확인하세요!

SSH 재시작

sudo systemctl restart sshd

새 포트로 접속 테스트

# 새 터미널에서 테스트 (기존 터미널 유지)
ssh -p 2222 webuser@<SERVER_IP>

5. 3단계 — 서비스 사용자 생성

root가 아닌 일반 사용자로 애플리케이션을 실행합니다.

사용자 생성

sudo adduser webuser
sudo usermod -aG sudo webuser

서비스 디렉터리 생성

sudo mkdir -p /var/www/example.com/{current,logs,backups}
sudo chown -R webuser:webuser /var/www/example.com
sudo chmod 755 /var/www/example.com

6. 4단계 — Docker 설치

Self-hosted Supabase는 Docker Compose로 실행됩니다.

Docker 공식 저장소 추가

# Docker GPG 키 추가
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg

# Docker 저장소 추가
echo \
  "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu \
  $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

Docker 설치

sudo apt update
sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

Docker 서비스 시작

sudo systemctl enable --now docker
sudo systemctl status docker

사용자에게 Docker 권한 부여

sudo usermod -aG docker webuser

⚠️ 주의: 로그아웃 후 다시 로그인해야 그룹 권한이 적용됩니다.

설치 확인

docker --version
docker compose version
docker run --rm hello-world

7. 5단계 — Self-hosted Supabase 설치

Supabase 저장소 클론

cd /root
git clone --depth 1 https://github.com/supabase/supabase.git
cd supabase/docker

환경 파일 생성

cp .env.example .env
chmod 600 .env

보안 키 생성

다음 값들을 반드시 변경해야 합니다:

# JWT Secret (최소 32자)
openssl rand -base64 32

# PostgreSQL 비밀번호
openssl rand -base64 16

# Dashboard 비밀번호
openssl rand -base64 16

# Secret Key Base
openssl rand -base64 32

.env 파일 편집

nano .env

주요 설정:

# PostgreSQL
POSTGRES_PASSWORD=<STRONG_PASSWORD>

# JWT
JWT_SECRET=<JWT_SECRET>
ANON_KEY=<ANON_KEY>  # JWT 기반 생성
SERVICE_ROLE_KEY=<SERVICE_ROLE_KEY>  # JWT 기반 생성

# Dashboard
DASHBOARD_USERNAME=supabase
DASHBOARD_PASSWORD=<STRONG_PASSWORD>

# Studio Secret
SECRET_KEY_BASE=<SECRET_KEY_BASE>

# Vault
VAULT_ENC_KEY=<VAULT_ENC_KEY>

JWT Key 생성 방법은 Supabase 공식 문서의 JWT 생성 가이드를 참고하세요.

Supabase 시작

cd /root/supabase/docker
docker compose pull
docker compose up -d

서비스 확인

docker compose ps

다음 서비스들이 Up 또는 healthy 상태여야 합니다:

  • supabase-db (PostgreSQL)
  • supabase-auth
  • supabase-kong (API Gateway)
  • supabase-rest (PostgREST)
  • supabase-storage
  • supabase-realtime
  • supabase-studio

8. 6단계 — Supabase 외부 URL 설정

Supabase가 외부에서 접근 가능한 URL을 인식하도록 설정합니다.

.env 파일 편집

nano /root/supabase/docker/.env

다음 값 수정:

# 외부에서 접근하는 Supabase API URL
SUPABASE_PUBLIC_URL=https://api.example.com
API_EXTERNAL_URL=https://api.example.com

# 메인 웹사이트 URL
SITE_URL=https://example.com

# OAuth 콜백 허용 URL
ADDITIONAL_REDIRECT_URLS=https://example.com/*,https://www.example.com/*

Auth 컨테이너 재시작

cd /root/supabase/docker
docker compose up -d --force-recreate auth

9. 7단계 — 데이터 마이그레이션 (선택)

기존 Supabase Cloud 또는 다른 PostgreSQL DB에서 데이터를 이전하는 단계입니다.

기존 DB 백업

# 원격 DB에서 백업
pg_dump -h <OLD_DB_HOST> -U postgres -d postgres --clean --if-exists > backup.sql

Self-hosted DB로 복원

# Docker 컨테이너로 복원
cat backup.sql | docker exec -i supabase-db psql -U postgres -d postgres

데이터 검증

docker exec supabase-db psql -U postgres -d postgres -c "\dt"
docker exec supabase-db psql -U postgres -d postgres -c "SELECT COUNT(*) FROM auth.users;"

10. 8단계 — DNS 설정

도메인의 DNS 레코드를 서버 IP로 연결합니다.

A 레코드 추가

@ (example.com)        → <SERVER_IP>
www                    → <SERVER_IP>
api                    → <SERVER_IP>

DNS 전파 확인

dig +short example.com
dig +short www.example.com
dig +short api.example.com

모두 서버 IP를 반환해야 합니다.

⚠️ 주의: DNS 전파는 최대 24~48시간이 걸릴 수 있습니다.


11. 9단계 — Nginx 및 HTTPS 설정

Nginx 설치

sudo apt install -y nginx certbot python3-certbot-nginx
sudo systemctl enable --now nginx

메인 웹사이트 설정

sudo nano /etc/nginx/sites-available/example.com

설정 내용:

server {
    listen 80;
    server_name example.com www.example.com;
    
    location / {
        proxy_pass http://127.0.0.1:3000;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        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;
        proxy_cache_bypass $http_upgrade;
    }
}

Supabase API 설정

sudo nano /etc/nginx/sites-available/api.example.com

설정 내용:

server {
    listen 80;
    server_name api.example.com;
    
    location / {
        proxy_pass http://127.0.0.1:8000;
        proxy_http_version 1.1;
        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;
    }
}

사이트 활성화

sudo ln -s /etc/nginx/sites-available/example.com /etc/nginx/sites-enabled/
sudo ln -s /etc/nginx/sites-available/api.example.com /etc/nginx/sites-enabled/

# 설정 테스트
sudo nginx -t

# Nginx 재시작
sudo systemctl reload nginx

HTTPS 인증서 발급

# 메인 웹사이트
sudo certbot --nginx -d example.com -d www.example.com

# Supabase API
sudo certbot --nginx -d api.example.com

자동 갱신 확인

sudo certbot certificates
sudo systemctl status certbot.timer
sudo certbot renew --dry-run

12. 10단계 — Node.js 및 PM2 설치

NVM 설치 (webuser로 실행)

su - webuser
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash

# 환경 변수 적용
export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh"

Node.js 설치

nvm install 20  # 또는 최신 LTS 버전
nvm alias default 20
node --version
npm --version

PM2 설치

npm install -g pm2
pm2 --version

PM2 자동 시작 설정

pm2 startup
# 출력된 명령어를 복사해서 실행 (sudo 포함)

13. 11단계 — SvelteKit 빌드 및 배포

로컬에서 빌드 (개발 PC)

cd your-sveltekit-project

# 의존성 설치
npm ci

# 프로덕션 빌드
npm run build

서버로 전송

# build 폴더와 package.json 전송
rsync -avz --delete \
  -e "ssh -p 2222" \
  --exclude '.env' \
  --exclude 'node_modules/' \
  ./build/ \
  webuser@<SERVER_IP>:/var/www/example.com/current/

rsync -avz \
  -e "ssh -p 2222" \
  ./package.json ./package-lock.json \
  webuser@<SERVER_IP>:/var/www/example.com/current/

서버에서 의존성 설치

ssh -p 2222 webuser@<SERVER_IP>
cd /var/www/example.com/current
npm ci --omit=dev

PM2로 애플리케이션 실행

# 환경 변수와 함께 실행
NODE_ENV=production PORT=3000 \
  pm2 start index.js --name myapp

# PM2 설정 저장
pm2 save

# 상태 확인
pm2 status
pm2 logs myapp --lines 50

애플리케이션 업데이트 시

# 새 빌드 파일 전송 후
cd /var/www/example.com/current
npm ci --omit=dev

# PM2 재시작 (무중단)
pm2 reload myapp --update-env

14. 12단계 — 환경 변수 설정

Frontend 환경 변수

서버의 .env 파일 생성:

nano /var/www/example.com/current/.env

내용:

# Supabase 연결
PUBLIC_SUPABASE_URL=https://api.example.com
PUBLIC_SUPABASE_ANON_KEY=<YOUR_ANON_KEY>

# 서버 전용 키 (PUBLIC_ 접두사 없음)
SUPABASE_SERVICE_ROLE_KEY=<YOUR_SERVICE_ROLE_KEY>

# 기타 서버 전용 시크릿
DATABASE_URL=<YOUR_DATABASE_URL>
API_SECRET_KEY=<YOUR_SECRET>

⚠️ 보안 주의:

  • PUBLIC_ 접두사가 있는 값만 브라우저에 노출됩니다
  • Service Role Key는 절대 PUBLIC_을 붙이지 마세요
  • .env 파일 권한: chmod 600 .env

15. 13단계 — 백업 자동화

백업 스크립트 생성

sudo nano /usr/local/bin/supabase-backup.sh

내용:

#!/bin/bash

BACKUP_DIR="/var/www/example.com/backups"
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
BACKUP_FILE="$BACKUP_DIR/backup_$TIMESTAMP.sql.gz"

# DB 백업
docker exec supabase-db pg_dump -U postgres -d postgres | gzip > "$BACKUP_FILE"

# 7일 이상 된 백업 삭제
find "$BACKUP_DIR" -name "backup_*.sql.gz" -mtime +7 -delete

echo "Backup completed: $BACKUP_FILE"

권한 설정:

sudo chmod +x /usr/local/bin/supabase-backup.sh

Cron 등록

sudo crontab -e

추가:

# 매일 새벽 2시 백업
0 2 * * * /usr/local/bin/supabase-backup.sh >> /var/www/example.com/logs/backup.log 2>&1

백업 테스트

sudo /usr/local/bin/supabase-backup.sh
ls -lh /var/www/example.com/backups/

16. 14단계 — 최종 검증

서비스 상태 확인

# Nginx
sudo systemctl status nginx
sudo nginx -t

# Docker (root로 실행)
cd /root/supabase/docker
docker compose ps

# PM2
su - webuser
pm2 status

외부 접근 테스트

# 메인 웹사이트
curl -I https://example.com

# Supabase API
curl -I https://api.example.com

브라우저 테스트


17. 운영 명령 모음

Nginx 관리

# 설정 테스트
sudo nginx -t

# 재시작 (무중단)
sudo systemctl reload nginx

# 로그 확인
sudo tail -f /var/log/nginx/access.log
sudo tail -f /var/log/nginx/error.log

Docker/Supabase 관리

cd /root/supabase/docker

# 상태 확인
docker compose ps

# 로그 확인
docker compose logs --tail=100 auth
docker compose logs --tail=100 kong

# 특정 서비스 재시작
docker compose restart auth

PM2 관리

su - webuser

# 상태 확인
pm2 status

# 로그 확인
pm2 logs myapp --lines 100

# 재시작 (무중단)
pm2 reload myapp

# 프로세스 목록 저장
pm2 save

인증서 갱신

# 수동 갱신
sudo certbot renew

# 인증서 확인
sudo certbot certificates

디스크 용량 확인

# 전체 디스크
df -h

# Docker 용량
docker system df

# 특정 디렉터리
du -sh /var/www/example.com/*
du -sh /root/supabase/*

18. 보안 체크리스트

  • SSH는 Key 방식으로만 접속 가능
  • Root 직접 로그인 비활성화
  • UFW 방화벽 활성화 (2222, 80, 443만 허용)
  • Docker 포트는 127.0.0.1에만 바인딩
  • Nginx는 리버스 프록시로만 사용
  • .env 파일 권한 600 설정
  • Service Role Key는 절대 브라우저에 노출 금지
  • HTTPS 인증서 자동 갱신 확인
  • DB 백업 자동화 및 외부 저장소 복제
  • 정기적인 보안 업데이트 (apt update && apt upgrade)

19. 트러블슈팅

Nginx 502 Bad Gateway

원인: Backend 서비스(PM2 또는 Docker)가 실행되지 않음

해결:

# PM2 확인
su - webuser
pm2 status
pm2 logs myapp

# Docker 확인
sudo docker compose -f /opt/supabase/docker/docker-compose.yml ps

PM2 프로세스가 재부팅 후 시작 안됨

해결:

su - webuser
pm2 startup  # 출력된 명령어 실행
pm2 save

Let's Encrypt 인증서 발급 실패

원인: DNS가 서버를 가리키지 않거나 방화벽이 80 포트 차단

해결:

# DNS 확인
dig +short example.com

# 방화벽 확인
sudo ufw status

# Nginx 80 포트 확인
sudo netstat -tlnp | grep :80

Docker 컨테이너 메모리 부족

해결:

# 메모리 확인
free -h

# 불필요한 컨테이너 정리
sudo docker system prune -a

# Docker 메모리 제한 설정 (docker-compose.yml)

20. 참고 자료


대상 환경: Ubuntu 22.04 LTS
난이도: 중급

이 가이드가 도움이 되었다면, 자신만의 프로덕션 서버 구축에 도전해 보세요! 💪

주파수 소통방 (0)

로딩 중...