Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 7 additions & 6 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,13 @@ REDIS_PORT=6379
# REDIS_PASSWORD=
# REDIS_URL=redis://localhost:6379/0 # Alternative to individual settings

# ── MinIO / S3 ─────────────────────────────────────────────────
MINIO_ENDPOINT=localhost:9000
MINIO_ACCESS_KEY=minioadmin
MINIO_SECRET_KEY=minioadmin
# MINIO_SECURE=false
# MINIO_BUCKET=code-interpreter-files
# ── S3 Storage (Garage) ────────────────────────────────────────
S3_ENDPOINT=localhost:3900
S3_ACCESS_KEY=GKminioadmin0000
S3_SECRET_KEY=minioadminsecret
# S3_SECURE=false
# S3_BUCKET=code-interpreter-files
# S3_REGION=garage

# ── Execution Limits ───────────────────────────────────────────
# MAX_EXECUTION_TIME=30 # Seconds (default: 30)
Expand Down
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
# ============================================
RUN mkdir -p /var/lib/code-interpreter/sandboxes && \
mkdir -p /mnt/data && \
mkdir -p /tmp/empty_proc
mkdir -p /var/lib/code-interpreter/empty_proc

RUN groupadd -g 1001 codeuser && \
useradd -u 1001 -g codeuser -m codeuser && \
Expand Down
51 changes: 20 additions & 31 deletions docker-compose.prod.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ services:
- SYS_ADMIN
# NET_ADMIN required to install iptables egress rules for sandbox uid
# when ENABLE_SANDBOX_NETWORK=true. Restricts sandbox traffic to the
# inline allowlist proxy and prevents SSRF to Redis/MinIO/etc.
# inline allowlist proxy and prevents SSRF to Redis/S3/etc.
- NET_ADMIN
security_opt:
- apparmor:unconfined
Expand All @@ -19,7 +19,7 @@ services:
- .env
environment:
- REDIS_HOST=redis
- MINIO_ENDPOINT=minio:9000
- S3_ENDPOINT=garage:3900
volumes:
- sandbox-data:/var/lib/code-interpreter/sandboxes
# Persistent skill-deps cache: pip/npm/go/cargo install here when
Expand All @@ -30,12 +30,13 @@ services:
# to the mounted files inside the container under /app/ssl.
- ${SSL_CERTS_PATH:-./ssl}:/app/ssl:ro
tmpfs:
- /tmp:size=512m,mode=1777,noexec,nosuid,nodev
- /app/data:size=100m
depends_on:
redis:
condition: service_healthy
minio-init:
condition: service_completed_successfully
garage:
condition: service_healthy
healthcheck:
test: ["CMD-SHELL", "curl -fs http://localhost:8000/health || curl -fsk https://localhost:8000/health"]
interval: 30s
Expand Down Expand Up @@ -63,43 +64,31 @@ services:
timeout: 5s
retries: 5

minio:
image: minio/minio:latest
container_name: code-interpreter-minio
# Garage S3-compatible object storage (replaces MinIO)
garage:
image: dxflrs/garage:v2.3.0
container_name: code-interpreter-garage
restart: unless-stopped
command: /garage server --single-node --default-bucket
ports:
- "127.0.0.1:${MINIO_PORT:-9000}:9000"
- "127.0.0.1:${MINIO_CONSOLE_PORT:-9001}:9001"
- "127.0.0.1:${S3_PORT:-3900}:3900"
- "127.0.0.1:${GARAGE_ADMIN_PORT:-3903}:3903"
environment:
MINIO_ROOT_USER: ${MINIO_ACCESS_KEY:-minioadmin}
MINIO_ROOT_PASSWORD: ${MINIO_SECRET_KEY:-minioadmin}
command: server /data --console-address ":9001"
GARAGE_DEFAULT_ACCESS_KEY: ${S3_ACCESS_KEY:-GKminioadmin0000}
GARAGE_DEFAULT_SECRET_KEY: ${S3_SECRET_KEY:-minioadminsecret}
GARAGE_DEFAULT_BUCKET: ${S3_BUCKET:-code-interpreter-files}
volumes:
- minio-data:/data
- garage-data:/var/lib/garage
- ./garage.toml:/etc/garage.toml
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:9000/minio/health/live"]
test: ["CMD", "/garage", "status"]
interval: 10s
timeout: 5s
retries: 5

minio-init:
image: minio/mc:latest
depends_on:
minio:
condition: service_healthy
entrypoint: >
/bin/sh -c "
mc alias set myminio http://minio:9000 $${MINIO_ACCESS_KEY:-minioadmin} $${MINIO_SECRET_KEY:-minioadmin};
mc mb --ignore-existing myminio/$${MINIO_BUCKET:-code-interpreter-files};
exit 0;
"
environment:
MINIO_ACCESS_KEY: ${MINIO_ACCESS_KEY:-minioadmin}
MINIO_SECRET_KEY: ${MINIO_SECRET_KEY:-minioadmin}
MINIO_BUCKET: ${MINIO_BUCKET:-code-interpreter-files}
start_period: 10s

volumes:
sandbox-data:
skill-deps:
redis-data:
minio-data:
garage-data:
51 changes: 19 additions & 32 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,19 +23,20 @@ services:
environment:
# Container-specific overrides (service discovery within compose network)
- REDIS_HOST=redis
- MINIO_ENDPOINT=minio:9000
- S3_ENDPOINT=garage:3900
volumes:
- sandbox-data:/var/lib/code-interpreter/sandboxes
# SSL_CERTS_PATH is a host path; SSL_CERT_FILE and SSL_KEY_FILE must point
# to the mounted files inside the container under /app/ssl.
- ${SSL_CERTS_PATH:-./ssl}:/app/ssl:ro
tmpfs:
- /tmp:size=512m,mode=1777,noexec,nosuid,nodev
- /app/data:size=100m
depends_on:
redis:
condition: service_healthy
minio-init:
condition: service_completed_successfully
garage:
condition: service_healthy
healthcheck:
test: ["CMD-SHELL", "curl -fs http://localhost:8000/health || curl -fsk https://localhost:8000/health"]
interval: 30s
Expand Down Expand Up @@ -65,44 +66,30 @@ services:
timeout: 5s
retries: 5

# MinIO for file storage
minio:
image: minio/minio:latest
container_name: code-interpreter-minio
# Garage S3-compatible object storage (replaces MinIO)
garage:
image: dxflrs/garage:v2.3.0
container_name: code-interpreter-garage
restart: unless-stopped
command: /garage server --single-node --default-bucket
ports:
- "127.0.0.1:${MINIO_PORT:-9000}:9000"
- "127.0.0.1:${MINIO_CONSOLE_PORT:-9001}:9001"
- "127.0.0.1:${S3_PORT:-3900}:3900"
- "127.0.0.1:${GARAGE_ADMIN_PORT:-3903}:3903"
environment:
MINIO_ROOT_USER: ${MINIO_ACCESS_KEY:-minioadmin}
MINIO_ROOT_PASSWORD: ${MINIO_SECRET_KEY:-minioadmin}
command: server /data --console-address ":9001"
GARAGE_DEFAULT_ACCESS_KEY: ${S3_ACCESS_KEY:-GKminioadmin0000}
GARAGE_DEFAULT_SECRET_KEY: ${S3_SECRET_KEY:-minioadminsecret}
GARAGE_DEFAULT_BUCKET: ${S3_BUCKET:-code-interpreter-files}
volumes:
- minio-data:/data
- garage-data:/var/lib/garage
- ./garage.toml:/etc/garage.toml
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:9000/minio/health/live"]
test: ["CMD", "/garage", "status"]
interval: 10s
timeout: 5s
retries: 5

# MinIO bucket initialization
minio-init:
image: minio/mc:latest
depends_on:
minio:
condition: service_healthy
entrypoint: >
/bin/sh -c "
mc alias set myminio http://minio:9000 $${MINIO_ACCESS_KEY:-minioadmin} $${MINIO_SECRET_KEY:-minioadmin};
mc mb --ignore-existing myminio/$${MINIO_BUCKET:-code-interpreter-files};
exit 0;
"
environment:
MINIO_ACCESS_KEY: ${MINIO_ACCESS_KEY:-minioadmin}
MINIO_SECRET_KEY: ${MINIO_SECRET_KEY:-minioadmin}
MINIO_BUCKET: ${MINIO_BUCKET:-code-interpreter-files}
start_period: 10s

volumes:
sandbox-data:
redis-data:
minio-data:
garage-data:
14 changes: 14 additions & 0 deletions garage.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
metadata_dir = "/var/lib/garage/meta"
data_dir = "/var/lib/garage/data"
db_engine = "sqlite"
replication_factor = 1

rpc_bind_addr = "[::]:3901"
rpc_secret = "0000000000000000000000000000000000000000000000000000000000000000"

[s3_api]
s3_region = "garage"
api_bind_addr = "[::]:3900"

[admin]
api_bind_addr = "[::]:3903"
4 changes: 2 additions & 2 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ redis==7.2.0
# SQLite async support for metrics
aiosqlite>=0.19.0

# MinIO/S3 client
minio==7.2.20
# S3 storage client (Garage/any S3-compatible backend)
boto3>=1.35.0

# Date/time parsing utilities
python-dateutil==2.9.0.post0
Expand Down
2 changes: 1 addition & 1 deletion src/api/exec.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ async def execute_code(
within the same session, whether the caller supplies `session_id` directly
or the orchestrator reuses a session through same-user file references or
`entity_id` continuity. State is stored in Redis (2 hour TTL) with
automatic archival to MinIO for long-term storage (7 day TTL).
automatic archival to S3 for long-term storage (configurable TTL).

Returns a streaming response that sends keepalive whitespace before the
JSON body to prevent client socket timeouts during long operations.
Expand Down
2 changes: 1 addition & 1 deletion src/api/files.py
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ async def upload_file(
# Sanitize filename to match what will be used in container
sanitized_name = OutputProcessor.sanitize_filename(file.filename)

# Store with sanitized name so MinIO, sandbox, and cleanup all use the same name
# Store with sanitized name so S3, sandbox, and cleanup all use the same name
file_id = await file_service.store_uploaded_file(
session_id=session_id,
filename=sanitized_name,
Expand Down
14 changes: 7 additions & 7 deletions src/api/health.py
Original file line number Diff line number Diff line change
Expand Up @@ -116,25 +116,25 @@ async def redis_health_check(_: str = Depends(verify_api_key)):
)


@router.get("/health/minio", summary="MinIO health check")
async def minio_health_check(_: str = Depends(verify_api_key)):
"""Check MinIO/S3 connectivity and performance."""
@router.get("/health/s3", summary="S3 storage health check")
async def s3_health_check(_: str = Depends(verify_api_key)):
"""Check S3 storage connectivity and performance."""
try:
result = await health_service.check_minio()
result = await health_service.check_s3()

if result.status == HealthStatus.UNHEALTHY:
return JSONResponse(status_code=503, content=result.to_dict())
else:
return JSONResponse(status_code=200, content=result.to_dict())

except Exception as e:
logger.error("MinIO health check failed", error=str(e))
logger.error("S3 health check failed", error=str(e))
return JSONResponse(
status_code=503,
content={
"service": "minio",
"service": "s3",
"status": "unhealthy",
"error": str(e) if settings.api_debug else "MinIO check failed",
"error": str(e) if settings.api_debug else "S3 check failed",
},
)

Expand Down
Loading
Loading