Space 관리
이 가이드에서는 huggingface_hub를 사용하여 Space 런타임(secrets, hardware, 볼륨)을 관리하는 방법을 알아봅니다.
Spaces 검색
Hub에서 search_spaces()를 사용하여 의미론적 검색으로 Spaces를 검색할 수 있습니다. 이 기능은 다중 단어 쿼리에 임베딩 기반 검색을, 단일 단어 쿼리에 전체 텍스트 검색을 사용합니다.
>>> from huggingface_hub import search_spaces
>>> results = list(search_spaces("generate image"))
>>> results[0]
SpaceSearchResult(id='mrfakename/Z-Image-Turbo', title='Z Image Turbo', sdk='gradio', likes=2867, ...)SDK 또는 태그로 결과를 필터링할 수 있습니다:
>>> results = search_spaces("chatbot", sdk="gradio", filter="mcp-server")또는 CLI를 통해:
>>> hf spaces search "generate image"
>>> hf spaces search "chatbot" --sdk gradio --limit 5간단한 예시: 시크릿과 하드웨어 구성
Hub에서 Space를 생성하고 설정하는 끝에서 끝까지의 예제를 보여 드리겠습니다.
Hub에서 Space 만들기
>>> from huggingface_hub import HfApi
>>> repo_id = "Wauplin/my-cool-training-space"
>>> api = HfApi()
# For example with a Gradio SDK
>>> api.create_repo(repo_id=repo_id, repo_type="space", space_sdk="gradio")Static Spaces는 모든 사용자에게 무료입니다. Gradio 및 Docker Spaces는 컴퓨트에서 실행되며, 무료
cpu-basic하드웨어의 경우 구독이 필요합니다(사용자는 PRO, 조직은 Team 또는 Enterprise). 유료 하드웨어의 경우 결제만 필요합니다(결제 수단 및 선불 크레딧). 구독이 없는 상태에서cpu-basic에서 Gradio 또는 Docker Space를 생성, 복제 또는 이동하면 결제 오류(HTTP 402)가 발생합니다. 자세한 내용은 pricing을 참조하세요.
Space를 템플릿에서 생성
빈 Space에서 시작하는 대신, Hub에서 제공하는 공식 템플릿(예: JupyterLab, Gradio 챗봇, Streamlit 앱 등) 중 하나를 기반으로 새로운 Space를 생성할 수 있습니다. list_space_templates()를 사용하여 사용 가능한 템플릿 목록을 확인한 후, 템플릿의 repo_id(또는 짧은 name)을 create_repo()의 space_template으로 전달합니다. space_sdk는 여전히 필요합니다. 템플릿은 파일을 초기화하고, SDK는 카드 메타데이터를 설정하기 때문입니다.
>>> from huggingface_hub import HfApi
>>> api = HfApi()
# List available templates
>>> for template in api.list_space_templates():
... print(f"{template.name} ({template.repo_id})")
Streamlit (streamlit/streamlit-template-space)
JupyterLab (SpacesExamples/jupyterlab)
chatbot (gradio-templates/chatbot)
...
# Create a Space from a template, by repo id or by short name
>>> api.create_repo(repo_id=repo_id, repo_type="space", space_template="SpacesExamples/jupyterlab")
>>> api.create_repo(repo_id=repo_id, repo_type="space", space_template="JupyterLab")일부 템플릿은 비공개로 설정하는 것이 권장됩니다(예: JupyterLab). 가시성을 명시적으로 설정하지 않으면, 이러한 Spaces는 자동으로 비공개로 생성됩니다.
CLI에서는 hf spaces templates와 hf repos create --template를 통해 동일한 기능을 사용할 수 있습니다:
# List available templates
>>> hf spaces templates
NAME REPO_ID SDK PREFERRED_PRIVATE
--------------- ---------------------------------------------- ------ -----------------
Streamlit streamlit/streamlit-template-space docker
JupyterLab SpacesExamples/jupyterlab docker ✔
Argilla argilla/argilla-template-space docker
Livebook livebook-dev/livebook docker
...
# Create a Space from a template
>>> hf repos create my-jupyterlab --type space --template jupyterlabSpace 복제
기존 Space를 기반으로 구축하고 싶거나 처음부터 시작하고 싶지 않은 경우 유용할 수 있습니다. 공개 Space의 구성/설정을 직접 제어하고 싶을 때도 유용합니다. 자세한 내용은 duplicate_repo()를 참조하세요.
>>> api.duplicate_repo("multimodalart/dreambooth-training", repo_type="space")원하는 솔루션을 사용하여 코드를 업로드하세요
로컬 폴더 src/를 컴퓨터에서 Space로 업로드하는 예시입니다:
>>> api.upload_folder(repo_id=repo_id, repo_type="space", folder_path="src/")이 단계에서 앱은 이미 Hub에서 실행 중이어야 합니다. 하지만 secrets와 업그레이드된 하드웨어를 구성하여 앱을 추가로 설정할 수 있습니다.
시크릿과 변수 구성
Space에는 작동에 필요한 비밀 키, 토큰 또는 변수가 필요할 수 있습니다. 자세한 내용은 문서를 참고하세요. 예를 들어, Space에서 생성된 이미지 데이터셋을 Hub에 업로드하기 위한 HF 토큰 등이 있습니다.
>>> api.add_space_secret(repo_id=repo_id, key="HF_TOKEN", value="hf_api_***")
>>> api.add_space_variable(repo_id=repo_id, key="MODEL_REPO_ID", value="user/repo")기존 secrets와 variables를 나열할 수 있습니다. Secret 값은 쓰기 전용이므로 키, 설명, 업데이트 타임스탬프만 반환됩니다:
>>> api.get_space_secrets(repo_id=repo_id)
{'HF_TOKEN': SpaceSecret(key='HF_TOKEN', description=None, updated_at=datetime.datetime(...))}
>>> api.get_space_variables(repo_id=repo_id)
{'MODEL_REPO_ID': SpaceVariable(key='MODEL_REPO_ID', value='user/repo', description=None, updated_at=...)}Secrets와 변수도 삭제할 수 있습니다:
>>> api.delete_space_secret(repo_id=repo_id, key="HF_TOKEN")
>>> api.delete_space_variable(repo_id=repo_id, key="MODEL_REPO_ID")Space 내부에서 secrets는 환경 변수(또는 Streamlit을 사용하는 경우 Streamlit Secrets Management)로 사용할 수 있습니다. API를 통해 가져올 필요가 없습니다!
Space 구성(시크릿 또는 하드웨어)의 변경 사항은 앱 재시작을 트리거합니다.
보너스: Space를 생성하거나 복제할 때 secrets와 variables를 설정하세요!
Secrets와 변수는 space를 생성하거나 복제할 때 설정할 수 있습니다:
>>> api.create_repo(
... repo_id=repo_id,
... repo_type="space",
... space_sdk="gradio",
... space_secrets=[{"key": "HF_TOKEN", "value": "hf_api_***"}, ...],
... space_variables=[{"key": "MODEL_REPO_ID", "value": "user/repo"}, ...],
... )>>> api.duplicate_repo(
... from_id=repo_id,
... repo_type="space",
... space_secrets=[{"key": "HF_TOKEN", "value": "hf_api_***"}, ...],
... space_variables=[{"key": "MODEL_REPO_ID", "value": "user/repo"}, ...],
... )하드웨어 구성
기본적으로 Space는 무료 cpu-basic 하드웨어에서 실행됩니다(Gradio 및 Docker Space의 경우 구독이 필요하며, 위 내용을 참조하세요). GPU에서 실행하려면 하드웨어를 업그레이드할 수 있습니다. Space 업그레이드를 위해 결제 수단과 선불 크레딧(또는 커뮤니티 보조금)이 필요합니다. 자세한 내용은 문서를 참조하세요.
# Use `SpaceHardware` enum
>>> from huggingface_hub import SpaceHardware
>>> api.request_space_hardware(repo_id=repo_id, hardware=SpaceHardware.T4_MEDIUM)
# Or simply pass a string value
>>> api.request_space_hardware(repo_id=repo_id, hardware="t4-medium")하드웨어 업데이트는 즉시 수행되지 않으며, Space가 서버에서 다시 로드되어야 합니다. 언제든 Space가 어떤 하드웨어에서 실행 중인지 확인하여 요청이 처리되었는지 살펴볼 수 있습니다.
>>> runtime = api.get_space_runtime(repo_id=repo_id)
>>> runtime.stage
"RUNNING_BUILDING"
>>> runtime.hardware
"cpu-basic"
>>> runtime.requested_hardware
"t4-medium"이제 완전히 구성된 Space를 갖추셨습니다. 사용을 마친 후에는 Space를 cpu-basic으로 다시 하향 조정하는 것을 잊지 마세요.
보너스: Space를 생성하거나 복제할 때 하드웨어를 요청하세요!
Space가 빌드되면 업그레이드된 하드웨어가 자동으로 할당됩니다.
>>> api.create_repo(
... repo_id=repo_id,
... repo_type="space",
... space_sdk="gradio",
... space_hardware="cpu-upgrade",
... space_sleep_time="7200", # 2 hours in secs
... )>>> api.duplicate_repo(
... from_id=repo_id,
... repo_type="space",
... space_hardware="cpu-upgrade",
... space_sleep_time="7200", # 2 hours in secs
... )ZeroGPU 할당량 추적
ZeroGPU Spaces는 온디맨드로 할당되는 공유 GPU에서 실행됩니다. ZeroGPU Space를 UI 또는 API를 통해 사용할 때마다 개인 할당량에서 GPU 사용 시간이 차감되며, 이 할당량은 주기적으로 초기화됩니다. ZeroGPU Spaces를 기반으로 앱, 에이전트 또는 MCP 서버를 구축하고 있다면 get_zero_gpu_quota()를 사용하여 사용량을 추적할 수 있습니다. 값은 GPU-초 단위로 표시됩니다.
>>> quota = api.get_zero_gpu_quota()
>>> quota
ZeroGpuQuota(base=2400, remaining=1810, resets_at=datetime.datetime(2026, 9, 30, 9, 12, 3, tzinfo=datetime.timezone.utc), overquota_used=0)
>>> if quota.remaining < 60:
... print(f"Less than 1 minute of ZeroGPU left. Quota resets at {quota.resets_at}.")resets_at은 마지막 리셋 이후 ZeroGPU를 사용하지 않은 경우 None입니다. 세밀한 권한을 가진 토큰을 사용하는 경우 “Billing > Read billing usage and payment method status” 권한이 있어야 합니다. 동일한 정보는 CLI에서 hf spaces zero-gpu quota를 통해 확인할 수 있습니다.
할당이 소진되면 PRO, Team 및 Enterprise 사용자는 선불 크레딧을 소모하여 ZeroGPU Spaces를 계속 사용할 수 있습니다. 크레딧은 billing settings에서 구매할 수 있습니다. 자세한 내용은 Extending quota with credits을 참조하세요.
Space 일시 중지 및 재시작
기본적으로 Space가 업그레이드된 하드웨어에서 실행 중이면 절대 중지되지 않습니다. 그러나 요금이 청구되는 것을 피하려면 사용하지 않을 때 일시중지하는 것이 좋습니다. pause_space()를 사용하면 가능합니다. 일시중지된 Space는 Space 소유자가 UI를 통해 또는 restart_space()를 사용하여 API를 통해 다시 시작할 때까지 비활성 상태가 유지됩니다. 일시중지 모드에 대한 자세한 내용은 이 섹션을 참조하세요.
# Pause your Space to avoid getting billed
>>> api.pause_space(repo_id=repo_id)
# (...)
# Restart it when you need it
>>> api.restart_space(repo_id=repo_id)다른 방법으로는 Space에 타임아웃을 설정하는 것입니다. Space가 타임아웃 기간보다 오래 비활성 상태면 수면 모드로 전환됩니다. Space에 방문하는 사용자는 Space를 다시 시작합니다. set_space_sleep_time()을 사용하여 타임아웃을 설정할 수 있습니다. 수면 모드에 대한 자세한 내용은 이 섹션을 참고하세요.
# Put your Space to sleep after 1h of inactivity
>>> api.set_space_sleep_time(repo_id=repo_id, sleep_time=3600)Note: ‘cpu-basic’ 하드웨어를 사용하는 경우 커스텀 sleep 시간을 설정할 수 없습니다. Space는 48시간 동안 비활성 상태가 되면 자동으로 일시 중지됩니다.
보너스: 하드웨어 요청 시 수면 시간 설정
Space가 빌드되면 업그레이드된 하드웨어가 자동으로 할당됩니다.
>>> api.request_space_hardware(repo_id=repo_id, hardware=SpaceHardware.T4_MEDIUM, sleep_time=3600)보너스: Space를 생성하거나 복제할 때 수면 시간을 설정하세요!
>>> api.create_repo(
... repo_id=repo_id,
... repo_type="space",
... space_sdk="gradio",
... space_hardware="t4-medium",
... space_sleep_time="3600",
... )>>> api.duplicate_repo(
... from_id=repo_id,
... repo_type="space",
... space_hardware="t4-medium",
... space_sleep_time="3600",
... )로그를 읽어서 실패한 Space 디버깅
Space의 빌드가 실패하거나 런타임에 충돌이 발생하면, 브라우저에서 확인하는 로그를 fetch_space_logs()를 통해 프로그래매틱으로도 조회할 수 있습니다. 이는 브라우저 열기가 불가능한 스크립트나 에이전틱 워크플로에서 특히 유용합니다.
# Drain the currently available run logs and return immediately (like `docker logs`)
>>> for line in api.fetch_space_logs(repo_id=repo_id):
... print(line, end="")
# Read the container build logs instead (useful when the Space is stuck in BUILD_ERROR)
>>> for line in api.fetch_space_logs(repo_id=repo_id, build=True):
... print(line, end="")
# Stream run logs in real time until the server closes the stream (Ctrl-C to stop)
>>> for line in api.fetch_space_logs(repo_id=repo_id, follow=True):
... print(line, end="")CLI에서도 동일한 기능을 사용할 수 있습니다:
hf spaces logs username/my-space # drain run logs
hf spaces logs username/my-space --build # read build logs
hf spaces logs username/my-space -f # stream in real time
hf spaces logs username/my-space -n 50 # last 50 lines onlySpace에 SSH 접속(개발 모드)
Dev Mode는 실행 중인 Space 컨테이너에 SSH로 접속하여 실시간 디버깅 및 개발을 수행할 수 있도록 합니다. 터미널에서 세션을 바로 열려면 hf spaces ssh를 사용하세요. Space에서 Dev Mode가 아직 활성화되지 않은 경우, CLI가 활성화하도록 프롬프트를 표시합니다(--auto를 전달하면 프롬프트를 건너뜁니다).
SSH 공개 키는 설정에 등록되어 있어야 합니다.
# SSH into a Space (enables Dev Mode if needed)
hf spaces ssh username/my-space
# Auto-enable Dev Mode without prompting
hf spaces ssh username/my-space --auto
# Print the SSH command without running it
hf spaces ssh username/my-space --dry-run
# Use a specific SSH key
hf spaces ssh username/my-space -i ~/.ssh/id_ed25519SSH 없이 hf spaces dev-mode를 사용하여 Dev Mode를 활성화할 수도 있습니다. 이 명령은 SSH, VS Code, Cursor, Windsurf에 대한 연결 지침을 출력합니다:
hf spaces dev-mode username/my-space
Dev Mode를 활성화하려면 PRO 또는 Team & Enterprise 플랜이 필요합니다. Dev Mode가 켜진 상태에서 구독이 만료된 경우와 같이 비활성화는 항상 허용됩니다:
hf spaces dev-mode username/my-space --stop
Space에 볼륨 마운트
Hub 리소스(모델, 데이터셋, 스토리지 버킷)를 Space 컨테이너의 볼륨으로 마운트할 수 있습니다. 이를 통해 코드로 리소스를 다운로드할 필요 없이 Space에서 해당 리소스에 직접 파일시스템으로 접근할 수 있습니다. Space를 생성하거나 복제할 때 볼륨을 직접 설정할 수 있습니다:
>>> from huggingface_hub import Volume
>>> api.create_repo(
... repo_id=repo_id,
... repo_type="space",
... space_sdk="gradio",
... space_volumes=[
... Volume(type="model", source="username/my-model", mount_path="/models", read_only=True),
... Volume(type="bucket", source="username/my-bucket", mount_path="/data"),
... ],
... )>>> api.duplicate_repo(
... from_id=repo_id,
... repo_type="space",
... space_volumes=[
... Volume(type="model", source="username/my-model", mount_path="/models", read_only=True),
... Volume(type="bucket", source="username/my-bucket", mount_path="/data"),
... ],
... )Space 런타임을 통해 현재 마운트된 볼륨을 확인할 수 있습니다:
>>> runtime = api.get_space_runtime(repo_id=repo_id)
>>> runtime.volumes
[Volume(type='model', source='username/my-model', mount_path='/models', read_only=True), ...]기존 Space의 볼륨을 업데이트해야 하는 경우 set_space_volumes()를 사용하세요. 이전에 마운트된 모든 볼륨이 대체됩니다.
>>> api.set_space_volumes(
... repo_id=repo_id,
... volumes=[
... Volume(type="model", source="username/my-model", mount_path="/models", read_only=True),
... Volume(type="dataset", source="username/my-dataset", mount_path="/data", read_only=True),
... Volume(type="bucket", source="username/my-bucket", mount_path="/output"),
... ],
... )Space에서 모든 볼륨을 제거하려면:
>>> api.delete_space_volumes(repo_id=repo_id)모델, 데이터셋, Spaces는 항상 읽기 전용으로 마운트됩니다. 읽기-쓰기 마운트는 스토리지 버킷에서만 지원합니다.
볼륨을 설정하면 이전에 마운트된 볼륨이 대체됩니다. 기존 목록에 볼륨을 추가하려면 먼저 런타임에서 현재 볼륨을 읽고 새 목록에 포함해야 합니다.
모든 볼륨 작업은 CLI에서도 사용할 수 있습니다:
# List current volumes
hf spaces volumes ls username/my-space
# Set (replace) volumes
hf spaces volumes set username/my-space \
-v hf://models/username/my-model:/models \
-v hf://buckets/username/my-bucket:/data
# Remove all volumes
hf spaces volumes delete username/my-space더 고급: Space를 일시적으로 업그레이드하세요!
Spaces는 다양한 사용 사례를 지원합니다. 때로는 특정 하드웨어에서 Space를 일시적으로 실행하여 작업을 수행한 후 종료하고 싶을 수 있습니다. 이 섹션에서는 온디맨드로 모델을 파인튜닝하기 위해 Spaces를 활용하는 방법을 살펴봅니다. 이는 해당 문제를 해결하는 방법 중 하나일 뿐이며, 제안 사항으로 간주하고 사용 사례에 맞게 조정해야 합니다.
모델을 fine-tuning하기 위한 Space가 있다고 가정해 보겠습니다. 이 Space는 model id와 dataset id를 입력으로 받는 Gradio 앱입니다. 워크플로우는 다음과 같습니다:
- (모델과 데이터셋을 사용자에게 입력하도록 요청합니다)
- Hub에서 모델을 로드합니다.
- Hub에서 데이터셋을 로드합니다.
- 데이터셋으로 모델을 Fine-tune합니다.
- 새 모델을 Hub에 업로드합니다.
Step 3.에서는 커스텀 하드웨어가 필요하지만, 유료 GPU에서 Space가 항상 실행되도록 하고 싶지 않은 경우입니다. 해결책은 학습을 위해 하드웨어를 동적으로 요청한 후, 이후에 이를 종료하는 것입니다. 하드웨어를 요청하면 Space가 재시작되므로, 앱은 현재 수행 중인 작업을 어떤 방식으로든 “기억”해야 합니다. 이를 위한 방법은 여러 가지가 있습니다. 이 가이드에서는 Dataset을 “작업 스케줄러”로 사용하는 하나의 해결책을 살펴보겠습니다.
앱 스켈레톤
여기 앱의 모습이 있습니다. 시작 시 태스크가 예약되어 있는지 확인하고, 예약되어 있다면 올바른 하드웨어에서 실행합니다. 완료되면 하드웨어를 cpu-basic CPU로 되돌리고 사용자에게 새 태스크를 요청합니다.
이러한 워크플로우는 일반적인 데모와 달리 동시 액세스를 지원하지 않습니다. 특히, 학습이 발생하면 인터페이스가 비활성화됩니다. 사용자가 본인뿐임을 보장하려면 저장소를 비공개로 설정하는 것이 좋습니다.
# Space will need your token to request hardware: set it as a Secret !
HF_TOKEN = os.environ.get("HF_TOKEN")
# Space own repo_id
TRAINING_SPACE_ID = "Wauplin/dreambooth-training"
from huggingface_hub import HfApi, SpaceHardware
api = HfApi(token=HF_TOKEN)
# On Space startup, check if a task is scheduled. If yes, finetune the model. If not,
# display an interface to request a new task.
task = get_task()
if task is None:
# Start Gradio app
def gradio_fn(task):
# On user request, add task and request hardware
add_task(task)
api.request_space_hardware(repo_id=TRAINING_SPACE_ID, hardware=SpaceHardware.T4_MEDIUM)
gr.Interface(fn=gradio_fn, ...).launch()
else:
runtime = api.get_space_runtime(repo_id=TRAINING_SPACE_ID)
# Check if Space is loaded with a GPU.
if runtime.hardware == SpaceHardware.T4_MEDIUM:
# If yes, finetune base model on dataset !
train_and_upload(task)
# Then, mark the task as "DONE"
mark_as_done(task)
# DO NOT FORGET: set back CPU hardware
api.request_space_hardware(repo_id=TRAINING_SPACE_ID, hardware=SpaceHardware.CPU_BASIC)
else:
api.request_space_hardware(repo_id=TRAINING_SPACE_ID, hardware=SpaceHardware.T4_MEDIUM)작업 스케줄러
작업 스케줄링은 다양한 방식으로 수행할 수 있습니다. 다음은 Dataset으로 저장된 간단한 CSV를 사용하여 이를 수행하는 예시입니다.
# Dataset ID in which a `tasks.csv` file contains the tasks to perform.
# Here is a basic example for `tasks.csv` containing inputs (base model and dataset)
# and status (PENDING or DONE).
# multimodalart/sd-fine-tunable,Wauplin/concept-1,DONE
# multimodalart/sd-fine-tunable,Wauplin/concept-2,PENDING
TASK_DATASET_ID = "Wauplin/dreambooth-task-scheduler"
def _get_csv_file():
return hf_hub_download(repo_id=TASK_DATASET_ID, filename="tasks.csv", repo_type="dataset", token=HF_TOKEN)
def get_task():
with open(_get_csv_file()) as csv_file:
csv_reader = csv.reader(csv_file, delimiter=',')
for row in csv_reader:
if row[2] == "PENDING":
return row[0], row[1] # model_id, dataset_id
def add_task(task):
model_id, dataset_id = task
with open(_get_csv_file()) as csv_file:
with open(csv_file, "r") as f:
tasks = f.read()
api.upload_file(
repo_id=repo_id,
repo_type=repo_type,
path_in_repo="tasks.csv",
# Quick and dirty way to add a task
path_or_fileobj=(tasks + f"\n{model_id},{dataset_id},PENDING").encode()
)
def mark_as_done(task):
model_id, dataset_id = task
with open(_get_csv_file()) as csv_file:
with open(csv_file, "r") as f:
tasks = f.read()
api.upload_file(
repo_id=repo_id,
repo_type=repo_type,
path_in_repo="tasks.csv",
# Quick and dirty way to set the task as DONE
path_or_fileobj=tasks.replace(
f"{model_id},{dataset_id},PENDING",
f"{model_id},{dataset_id},DONE"
).encode()
)