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
브라우저 테스트
- https://example.com - 메인 페이지 정상 로드
- https://api.example.com - Supabase Studio 로그인
- 회원가입/로그인 기능 테스트
- DB 읽기/쓰기 테스트
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. 참고 자료
- Supabase Self-hosting 공식 문서
- SvelteKit adapter-node 문서
- PM2 공식 문서
- Nginx 설정 가이드
- Let's Encrypt 인증서 가이드
- Docker Compose 문서
대상 환경: Ubuntu 22.04 LTS
난이도: 중급
이 가이드가 도움이 되었다면, 자신만의 프로덕션 서버 구축에 도전해 보세요! 💪


주파수 소통방 (0)
로딩 중...