Spaces:
Sleeping
Download docs/deployment.md from Sid-WC121/visual-notebook: direct link, hf CLI and curl.
- Browser
- Download file 10.9 kB
-
https://huggingface.co/spaces/Sid-WC121/visual-notebook/resolve/main/docs/deployment.md
- Command line
-
hf download hf://spaces/Sid-WC121/visual-notebook/docs/deployment.md
-
curl -L -o deployment.md https://huggingface.co/spaces/Sid-WC121/visual-notebook/resolve/main/docs/deployment.md
Deployment Guide
This document covers all deployment options for the Visual Notebook, including Docker builds, Docker Compose for development, CI/CD pipelines, Hugging Face Spaces deployment, environment configuration, and production considerations.
Docker Build Options
The project provides three Dockerfile configurations for different deployment scenarios:
Combined Build (Dockerfile)
The combined Dockerfile builds both the frontend and backend into a single image. This is the recommended approach for most deployments, including HF Spaces.
Build stages:
frontend-build (Node.js 20): Installs npm dependencies and builds the React application with
npm run build. The output is written tofrontend/dist/.Runtime (Python 3.11): Installs the backend using
uv pip install --system -e ., copies the built frontend from the previous stage into/app/static, and configures environment variables.
# Stage 1: Build frontend
FROM node:20-slim AS frontend-build
WORKDIR /app/frontend
COPY frontend/package.json frontend/package-lock.json* ./
RUN npm install
COPY frontend/ ./
RUN npm run build
# Stage 2: Runtime
FROM python:3.11-slim
WORKDIR /app
RUN pip install uv
COPY backend/pyproject.toml backend/
COPY backend/visual_notebook/ backend/visual_notebook/
RUN cd backend && uv pip install --system -e .
COPY datasets/ /app/datasets/
COPY --from=frontend-build /app/frontend/dist /app/static
ENV DATA_DIR=/app/datasets
ENV CHECKPOINT_DIR=/app/checkpoints
ENV CORS_ORIGINS=*
ENV HOST=0.0.0.0
ENV PORT=7860
EXPOSE 7860
CMD ["uvicorn", "visual_notebook.main:app", "--host", "0.0.0.0", "--port", "7860", "--workers", "1"]
Build and run:
docker build -t visual-notebook .
docker run -p 7860:7860 -v $(pwd)/datasets:/app/datasets visual-notebook
Standalone Backend (Dockerfile.backend)
The backend-only Dockerfile skips the frontend build entirely. It is intended for deployments where the frontend is served separately (e.g., by a CDN or Nginx).
FROM python:3.11-slim
WORKDIR /app
RUN pip install uv
COPY backend/pyproject.toml backend/
COPY backend/visual_notebook/ backend/visual_notebook/
COPY datasets/ /app/datasets/
RUN cd backend && uv pip install --system -e .
ENV DATA_DIR=/app/datasets
ENV CHECKPOINT_DIR=/app/checkpoints
ENV CORS_ORIGINS=*
ENV HOST=0.0.0.0
ENV PORT=7860
EXPOSE 7860
CMD ["uvicorn", "visual_notebook.main:app", "--host", "0.0.0.0", "--port", "7860", "--workers", "1"]
Frontend-Only (frontend/Dockerfile)
The frontend-only Dockerfile builds the React application and serves it with Nginx:
FROM node:20-slim AS build
WORKDIR /app
COPY package.json package-lock.json* ./
RUN npm install
COPY . .
RUN npm run build
FROM nginx:alpine
COPY --from=build /app/dist /usr/share/nginx/html
COPY nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
The Nginx configuration (frontend/nginx.conf) is set up to proxy API requests to the backend and serve static files for all other routes.
Docker Compose for Development
The docker-compose.yml defines two services for local development:
services:
backend:
build:
context: .
dockerfile: Dockerfile.backend
ports:
- "8000:7860"
volumes:
- ./datasets:/app/datasets
- ./checkpoints:/app/checkpoints
environment:
- DATA_DIR=/app/datasets
- CHECKPOINT_DIR=/app/checkpoints
- CORS_ORIGINS=http://localhost:5173,http://localhost:3000
frontend:
build:
context: ./frontend
dockerfile: Dockerfile
ports:
- "5173:80"
environment:
- VITE_API_URL=http://localhost:8000
depends_on:
- backend
Usage:
# Start both services
docker compose up --build
# Backend available at http://localhost:8000
# Frontend available at http://localhost:5173
The volume mounts ensure that uploaded datasets and checkpoints persist across container restarts.
GitHub Actions CI/CD
The recommended GitHub Actions workflow follows a test-build-deploy pipeline:
Workflow Stages
- Test: Run pytest against the backend with
uv run pytest. - Build: Build the Docker image using the combined Dockerfile.
- Deploy: Push the image to a container registry and deploy.
Example Workflow
name: CI/CD
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: astral-sh/setup-uv@v4
with:
version: "latest"
- name: Install dependencies
run: uv sync
- name: Run tests
run: uv run pytest backend/visual_notebook/tests/ -v
build:
needs: test
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'
steps:
- uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push
uses: docker/build-push-action@v5
with:
context: .
push: true
tags: ghcr.io/${{ github.repository }}:latest
cache-from: type=gha
cache-to: type=gha,mode=max
UV Caching
The setup-uv action caches the uv installation and downloaded packages. For Docker builds, the cache-from and cache-to options use GitHub Actions cache to persist Docker layers between builds, significantly reducing build times for subsequent pushes.
GitLab CI/CD Pipeline
For GitLab deployments, the following pipeline structure is recommended:
stages:
- test
- build
- deploy
test:
stage: test
image: python:3.11-slim
before_script:
- pip install uv
- uv sync
script:
- uv run pytest backend/visual_notebook/tests/ -v
build:
stage: build
image: docker:24
services:
- docker:24-dind
script:
- docker build -t $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA .
- docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
only:
- main
deploy:
stage: deploy
image: alpine:latest
script:
- apk add --no-cache curl
- curl -X POST "$DEPLOY_WEBHOOK_URL"
only:
- main
Hugging Face Spaces Deployment
The combined Dockerfile is designed to be compatible with HF Spaces requirements:
- Port 7860: HF Spaces expects the application to listen on port 7860. The Dockerfile
EXPOSE 7860and the uvicorn command use this port. - Static file serving: The backend serves the frontend from
/app/static, which is populated during the Docker build. - Environment variables: HF Spaces allows setting secrets and environment variables through the Space settings UI.
Deployment Steps
- Create a new Docker Space on Hugging Face.
- Clone the Space repository.
- Copy the project files (or add the Space as a git remote).
- Push to the Space repository. HF Spaces will build and deploy automatically.
Space Configuration
Set the following secrets in the Space settings:
| Variable | Value |
|---|---|
DATA_DIR |
/app/datasets |
CHECKPOINT_DIR |
/app/checkpoints |
CORS_ORIGINS |
* (or your specific domain) |
MAX_UPLOAD_SIZE |
524288000 (500 MB) |
Persistent Storage
HF Spaces offers persistent storage for paid tiers. To enable it, mount a persistent volume at /app/datasets and /app/checkpoints. Without persistent storage, uploaded files are lost when the Space restarts.
Environment Variable Configuration
All configuration is managed through environment variables, read by the Settings dataclass in config.py:
| Variable | Default | Description |
|---|---|---|
DATA_DIR |
datasets |
Directory for uploaded CSV files |
CHECKPOINT_DIR |
checkpoints |
Directory for session checkpoints |
MAX_UPLOAD_SIZE |
524288000 (500 MB) |
Maximum CSV file size in bytes |
PREVIEW_ROWS |
50 |
Number of rows returned by default in head() |
SAMPLE_ROWS |
10000 |
Number of rows for Polars CSV sampling |
CORS_ORIGINS |
http://localhost:5173,http://localhost:3000 |
Comma-separated allowed origins |
HOST |
0.0.0.0 |
Server bind address |
PORT |
8000 |
Server bind port (7860 in Docker) |
Production Considerations
Session Storage
The current SessionStore is in-memory, meaning all session data is lost when the server restarts. For production deployments, consider replacing it with a persistent store:
- Redis: Store session metadata in Redis with TTL-based expiration. DuckDB tables would need to be exported to Parquet and reloaded on session restore.
- Database-backed: Use a lightweight database (SQLite, PostgreSQL) to store session state and checkpoint references.
- File-based: Serialize session metadata to JSON files in the checkpoint directory.
Static File Serving
In production, the backend serves static files from /app/static. For high-traffic deployments, consider:
- Using a CDN in front of the application for static asset caching.
- Serving static files directly from Nginx rather than through FastAPI's
StaticFilesmiddleware. - Enabling gzip compression for JS and CSS assets.
Health Checks
The /health endpoint returns a JSON response with status and version. Configure your load balancer or orchestrator to use this endpoint for health monitoring:
{"status": "ok", "version": "0.1.0"}
For more comprehensive health checks, consider extending the endpoint to verify DuckDB connectivity and disk space availability.
Resource Limits
DuckDB operates entirely in memory. For datasets that exceed available RAM:
- Set
max_memoryin the DuckDB connection to enable disk spilling:duckdb.connect(":memory:", config={"memory_limit": "2GB"}). - Use
PREVIEW_ROWSandSAMPLE_ROWSto limit data sent to the frontend. - Monitor memory usage and set container memory limits appropriately.
Scaling Considerations
The current architecture is single-process and single-session-per-container. To scale horizontally:
- Session affinity: Use cookie-based sticky sessions to route each user to the same backend instance.
- Shared storage: Use a networked filesystem (EFS, S3) for
DATA_DIRso all instances can access uploaded files. - Connection pooling: If moving to a shared DuckDB instance, implement connection pooling to manage concurrent access.
- Load balancing: Place multiple backend instances behind a load balancer with health check routing.
For most use cases, a single instance with sufficient RAM (4-8 GB) handles dozens of concurrent users comfortably, given that CSV analysis is typically an interactive, low-concurrency workload.