1. 개요
계기
이 글은 Supabase 무료 티어 90일 비활성으로 일시정지된 프로젝트를 백업 파일로 복원하는 과정을 정리하기 위해 작성했다.
2024년에 회사에서 LLM을 활용한 RAG 테스트 프로젝트를 진행하면서 Supabase를 알게 되었고 Hybrid Search 등을 지원하여 프로젝트에 사용했었다. 한동안 사용 안 하다가 다시 보려고 접속했는데 프로젝트가 일시정지 상태로 나오고 있는 것이었다.
찾아보니 Supabase 무료 티어는 90일 이상 비활성 상태면 자동으로 일시정지된다고 한다... 말이 일시정지인거지 그냥 사용할 수가 없는 상태로 된다.
다행히 백업 파일은 일시정지된 프로젝트의 Dashboard에서 다운받을 수 있었고(.backup.gz, .storage.zip) 새 프로젝트를 만들어서 복원하기로 했다.
환경
- 운영체제: macOS
- Docker Desktop, Supabase CLI, PostgreSQL 클라이언트(
psql) - 다운로드 받은 백업 파일:
db_cluster-*.backup.gz,*.storage.zip
이번에 복원한 범위는 데이터베이스(테이블·함수·트리거·인덱스 전부, 사용자 3명·문서 124개 등), Storage 메타데이터·실제 파일(객체 520개, 48.9MB). 로컬에서 먼저 테스트한 뒤 원격 복원했음.
2. 공식문서 방법 시도 및 실패
먼저 공식문서를 보고 그대로 따라해봤음.
# 1. PostgreSQL 설치
brew install postgresql
설치 확인은 아래처럼 하면 됨.
pg_restore --version
# pg_restore (PostgreSQL) 14.20 (Homebrew)
(1) 첫 번째 시도 - Direct Connection
새 프로젝트를 만들고 Dashboard에서 Connection string을 확인한 뒤 아래처럼 넣어야 함.
postgresql://postgres:[YOUR-PASSWORD]@db.xnevbeeifkumavhbhepk.supabase.co:5432/postgres
비밀번호를 넣어줘야해서 비밀번호 설정하고 복원 시도했음.
export PGPASSWORD="your_password"
psql \
-h db.xnevbeeifkumavhbhepk.supabase.co \
-p 5432 \
-U postgres \
-d postgres \
< db_cluster-28-09-2025@15-06-50.backup
근데 아래와 같이 주소 관련해서 알 수 없다는 에러가 발생했다.
psql: error: could not translate host name "db.xnevbeeifkumavhbhepk.supabase.co" to address: nodename nor servname provided, or not known
ping과 nslookup으로 확인해봐도 같은 에러가 나서 났다.
ping db.xnevbeeifkumavhbhepk.supabase.co
# ping: cannot resolve db.xnevbeeifkumavhbhepk.supabase.co: Unknown host
알고보니 Direct connection은 IPv4 유료 플랜이 필요했던 것이다...

(2) 두 번째 시도 - Session Pooler로 변경
Connection 방법을 Direct에서 Session pooler로 바꿧다.

세션 풀러를 선택 하니 IPv4 주소가 제대로 나왔다.

그래서 Session pooler로 재시도 했다.
gunzip -c db_cluster-28-09-2025@15-06-50.backup.gz | \
psql \
-h aws-1-ap-northeast-1.pooler.supabase.com \
-p 5432 \
-U postgres.xnevbeeifkumavhbhepk \
-d postgres
(3) 세 번째 시도 - Session pooler 에러 폭탄 발생
이번엔 연결은 되는데 복원 과정에서 에러가 폭탄처럼 쏟아졌다.
Role 관련 에러 (수백 개)
ERROR: role "anon" already exists
ERROR: "anon" is a reserved role, only superusers can modify it
ERROR: role "authenticated" already exists
ERROR: role "supabase_admin" already exists
ERROR: permission denied to alter role
Supabase가 자동으로 생성하는 Role들과 충돌한다는 에러...
Schema 충돌
ERROR: schema "auth" already exists
ERROR: schema "storage" already exists
ERROR: permission denied for schema auth
새 프로젝트에 이미 존재하는 스키마를 백업 파일이 재생성하려고 시도..
Permission Denied
ERROR: permission denied for schema auth
ERROR: must be owner of table audit_log_entries
ERROR: permission denied for function get_auth
ERROR: permission denied to change default privileges
Supabase 권한 부족..
등등 에러 메시지가 계속 스크롤되면서 올라갔고 수백 개는 넘는 것 같았다..
에러 원인을 정리하자면 4가지 정도인 것 같다.
- 백업 파일이 full dump 방식이라 모든 CREATE 명령 포함
- 새 Supabase 프로젝트는 이미 기본 스키마, Role, 권한이 자동 생성되어 있음
- 백업 파일이 이미 존재하는 것들을 재생성하려다가 충돌
- 에러는 무시해도 되는 경고들이지만 핵심 데이터 복원이 제대로 안될 위험이 있음
그래서 다른 방법이 없나 하고 구글 검색이나 레딧을 찾아보니 저렇게 직접 psql로 넣는게 아니라 로컬 supabase에서 작업하고 복원했다는 글이 있어서 그렇게 진행해보기로 했다.
3. 로컬 Supabase로 원격 Supabase 복원 시도
(1) 로컬 환경 준비
필수 도구 설치
postgresql 버전은 dashboard를 참조해서 버전을 세팅했음.
# Supabase CLI 설치 (Homebrew)
brew install supabase/tap/supabase
# PostgreSQL 클라이언트 설치
brew install postgresql@15
# Docker Desktop 실행
open -a Docker
Docker 실행 확인
Docker가 정상적으로 실행 중인지 확인함.
docker ps
(2) 로컬 Supabase 환경 구축
Supabase 프로젝트 초기화
작업 디렉토리를 만들고 Supabase를 초기화함.
# 작업 디렉토리 생성
mkdir supabase-restore && cd supabase-restore
# Supabase 초기화
supabase init
PostgreSQL 버전 설정
중요: 백업 파일과 로컬 환경의 PostgreSQL 버전이 일치해야 한다.
내 백업 파일은 PostgreSQL 15로 생성되었기 때문에 로컬도 15로 맞춰줬다.
supabase/config.toml 파일을 아래처럼 수정했음.
[db]
major_version = 15
추가로 버전 파일도 생성
mkdir -p supabase/.temp
echo "15.6.1.115" > supabase/.temp/postgres-version
로컬 Supabase 시작
supabase start
정상적으로 시작되면 아래와 같은 정보가 나온다.
Started supabase local development setup.
API URL: http://localhost:54321
DB URL: postgresql://postgres:postgres@localhost:54322/postgres
Studio URL: http://localhost:54323
이렇게 되면 일단 로컬 Supabase 세팅은 끝이다.
(3) 백업 파일 복원
SQL 덤프 추출
백업 파일을 SQL 텍스트로 변환한다.
pg_restore -f postgres_restore.sql -d postgres db_cluster-*.backup
로컬 데이터베이스에 복원
추출한 SQL 파일을 로컬 PostgreSQL에 적용
psql 'postgresql://postgres:postgres@localhost:54322/postgres' \
-f postgres_restore.sql 2>&1 | tee restore.log
* 에러사항 무시!
복원하면 아까 Supabase 원격으로 복원하는 것처럼 에러 메시지가 엄청 쏟아지는데 걱정하지말고 대부분 무시해도 된다.
schema already exists: Supabase가 이미 만들어둔 스키마(auth,storage등)와 충돌permission denied: Supabase 관리 영역 권한 문제unrecognized configuration parameter: PostgreSQL 버전 차이
이런 에러들은 대부분 무시해도 되고 실제 데이터(테이블, 함수, 트리거)는 정상적으로 복원된다.
Storage 메타데이터 복원
여기서 또 문제가 발생했다.
문제 상황
storage.objects 테이블 복원 시 다음 에러가 남.
ERROR: column "level" does not exist
원인: 백업 파일에는 level 컬럼이 있는데 새 Supabase에는 없다는 것이다.
해결 방법
그래서 sed로 해당 컬럼을 제거했다.
# storage.objects COPY 문 추출
grep -A 100000 "COPY storage.objects" postgres_restore.sql > storage_objects.sql
# level 컬럼 제거
sed 's/\t[0-9]\+$//' storage_objects.sql > storage_objects_fixed.sql
# 수정된 파일 적용
psql 'postgresql://postgres:postgres@localhost:54322/postgres' \
-f storage_objects_fixed.sql
복원 확인
테이블이 제대로 복원되었는지 확인 진행
psql 'postgresql://postgres:postgres@localhost:54322/postgres' -c "
SELECT
schemaname,
tablename
FROM pg_tables
WHERE schemaname IN ('public', 'auth', 'storage')
ORDER BY schemaname, tablename;
"
함수도 확인
psql 'postgresql://postgres:postgres@localhost:54322/postgres' -c "
SELECT
n.nspname as schema,
p.proname as function_name
FROM pg_proc p
JOIN pg_namespace n ON p.pronamespace = n.oid
WHERE n.nspname = 'public'
ORDER BY function_name;
"
(4) 원격 Supabase 프로젝트로 복원
원격 데이터베이스 연결 정보 확인
로컬 테스트가 끝났으면 이제 원격 프로젝트로 복원하면 끝이다.
Supabase Dashboard > Project Settings > Database > Connection string
아까랑 똑같이 진행하는데 Direct 방식은 유료로 해야하니 Session pooler 나 Transaction pooler를 사용해서 진행하면 된다.
* Connection string 형식
postgresql://postgres.[project-ref]:[password]@aws-1-[region].pooler.supabase.com:5432/postgres
SQL 파일 -> 원격 Supabase 적용
로컬에서 사용한 SQL 파일을 그대로 원격에 적용한다.
# 전체 SQL 적용
psql 'postgresql://postgres.[project-ref]:[password]@aws-1-[region].pooler.supabase.com:5432/postgres' \
-f postgres_restore.sql 2>&1 | tee remote_restore.log
# Storage 메타데이터 적용
psql 'postgresql://postgres.[project-ref]:[password]@aws-1-[region].pooler.supabase.com:5432/postgres' \
-f storage_objects_fixed.sql 2>&1 | tee remote_storage.log
로컬과 마찬가지로 에러 메시지 많이 나오는데 무시하면 된다.
대시보드 복원 확인
복원 후 커스텀 함수 및 트리거 등 제대로 생성되었는지 확인한다.
Dashboard에서 확인
- Database > Functions에서 함수 목록 확인
- SQL Editor에서 직접 쿼리 실행
SQL Editor에서 함수 조회
-- 함수 목록 조회
SELECT
n.nspname as schema,
p.proname as function_name,
pg_get_function_arguments(p.oid) as arguments
FROM pg_proc p
JOIN pg_namespace n ON p.pronamespace = n.oid
WHERE n.nspname = 'public'
ORDER BY function_name;
특정 함수의 전체 정의를 확인하려면 아래 쿼리를 실행한다.
-- 특정 함수 정의 확인
SELECT pg_get_functiondef(oid)
FROM pg_proc
WHERE proname = 'hybrid_search';
Storage 파일 업로드 (S3 버킷)
주의: 데이터베이스에는 메타데이터(storage.objects 테이블)만 복원되고 실제 파일은 별도로 업로드해야 한다.
복원할 때가 아니라 기존에 supabase에 파일 업로드할 때도 버킷을 사용했기 때문에 똑같이 해주면 된다.
방법 1: Dashboard 사용
- Supabase Dashboard > Storage
- 각 버킷(
rag,customer_file등) 선택 - 파일 수동 업로드
방법 2: Supabase CLI, Storage API 키 사용 (데이터 많을 때)
(5) 애플리케이션에서 테스트
환경 변수 설정
이제 애플리케이션이 새 프로젝트를 바라보도록 환경 변수를 바꿔준다.
Node.js 프로젝트 기준으로 .env 파일을 아래처럼 수정함.
# .env 파일
MODE=development
SERVER_PORT=3000
# Supabase Configuration
SUPABASE_URL=https://[project-ref].supabase.co
SUPABASE_ANON_KEY=your_anon_key_here
JWT_SECRET=your_jwt_secret_here
# OpenAI API Key
OPENAI_API_KEY=your_openai_api_key
# 기타 API Keys
API_2_PDF=your_api2pdf_key
KYUNGRINARA_API_KEY=your_kyungrinara_key
Supabase 대시보드 API Key 위치
Supabase Dashboard > Settings > API
anonkey: 클라이언트에서 사용하는 공개 키service_rolekey: 서버 전용, 모든 권한 (클라이언트 노출 절대 금지)- JWT Secret: 토큰 검증용
연결 테스트
// supabase-test.js
const { createClient } = require('@supabase/supabase-js');
require('dotenv').config();
const supabase = createClient(
process.env.SUPABASE_URL,
process.env.SUPABASE_ANON_KEY
);
async function testConnection() {
const { data, error } = await supabase
.from('documents')
.select('*')
.limit(1);
if (error) {
console.error('연결 실패:', error);
} else {
console.log('연결 성공!');
console.log('데이터:', data);
}
}
testConnection();
실행
node supabase-test.js
4. 결과
힘든 과정을 거쳐서 2024년도에 RAG 테스트 프로젝트에 사용한 데이터를 전부 복구했다!
복원된 데이터
- 사용자: 3명 (auth.users)
- 문서: 124개 (public.documents)
- 욕설 데이터: 8,037개 (public.profanity_data)
- 가이드 문서: 409개 (public.combine_guide_documents)
- 이미지 메타데이터: 248개 (public.image_metadata)
- 함수: 207개 (hybrid_search 등 커스텀 함수 포함)
Storage 확인
- 버킷: 2개 (customer_file, rag)
- 객체 메타데이터: 520개
- 실제 파일: 48.9MB
5. 정리
Supabase 프리티어 90일 비활성으로 일시정지된 프로젝트를 백업 파일로 복원하는 과정을 정리했다.
다른 무료 호스팅들의 경우 멈춤 상태로 있어도 그냥 활성화하면 작동하는 경우가 많아서 크게 신경 안썼는데 Supabase는 정지시켜버려서 당황했다.. 그리고 다시 복원하는데 조금 애를 먹었다.
2024년도에 사내에서 LLM을 활용한 RAG 프로젝트 진행할 때 한창 Supabase 얘기가 많이 나와서 supabase로 먼저 테스트를 진행했었다. 최근에도 다양한 프로젝트에서 supabase를 많이 활용하는걸로 아는데 90일 비활성화 부분에 대해서는 잘 생각을 해봐야할 것 같다. 주기적으로 사용하지 않으면 잠겨버리니..
앞으로 진행한다면 로컬에 띄워두고 테스트하고 실제 배포 테스트할 때 원격 Supabase를 사용해야겠다.
핵심 포인트
- 무료 티어는 90일 비활성 시 일시정지
- 공식문서 방법은 에러로 실패할 수 있음
pg_restore로 SQL 뽑아서 수정 후 복원하면 안정적- 백업 파일에 맞게 PostgreSQL 버전 맞춰야 함
- 로컬 테스트 후 원격 복원
- Connection String은 Session pooler 사용 (Direct는 IPv4 유료)
- 버킷에 업로드한 Storage 파일은 별도 업로드 필요 (메타데이터만 복원)
- 복원 후에 Custom Functions 동작 확인 필수 (
hybrid_search같은 것들)
