Skip to content

Commit 0fc9cc3

Browse files
authored
Merge pull request #90 from usnavy13/feat/migrate-minio-to-garage-s3
Replace MinIO with S3-compatible storage (Garage)
2 parents ff69603 + 76987db commit 0fc9cc3

39 files changed

Lines changed: 611 additions & 549 deletions

.env.example

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -35,12 +35,13 @@ REDIS_PORT=6379
3535
# REDIS_PASSWORD=
3636
# REDIS_URL=redis://localhost:6379/0 # Alternative to individual settings
3737

38-
# ── MinIO / S3 ─────────────────────────────────────────────────
39-
MINIO_ENDPOINT=localhost:9000
40-
MINIO_ACCESS_KEY=minioadmin
41-
MINIO_SECRET_KEY=minioadmin
42-
# MINIO_SECURE=false
43-
# MINIO_BUCKET=code-interpreter-files
38+
# ── S3 Storage (Garage) ────────────────────────────────────────
39+
S3_ENDPOINT=localhost:3900
40+
S3_ACCESS_KEY=GKminioadmin0000
41+
S3_SECRET_KEY=minioadminsecret
42+
# S3_SECURE=false
43+
# S3_BUCKET=code-interpreter-files
44+
# S3_REGION=garage
4445

4546
# ── Execution Limits ───────────────────────────────────────────
4647
# MAX_EXECUTION_TIME=30 # Seconds (default: 30)

Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -233,7 +233,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
233233
# ============================================
234234
RUN mkdir -p /var/lib/code-interpreter/sandboxes && \
235235
mkdir -p /mnt/data && \
236-
mkdir -p /tmp/empty_proc
236+
mkdir -p /var/lib/code-interpreter/empty_proc
237237

238238
RUN groupadd -g 1001 codeuser && \
239239
useradd -u 1001 -g codeuser -m codeuser && \

docker-compose.prod.yml

Lines changed: 20 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ services:
99
- SYS_ADMIN
1010
# NET_ADMIN required to install iptables egress rules for sandbox uid
1111
# when ENABLE_SANDBOX_NETWORK=true. Restricts sandbox traffic to the
12-
# inline allowlist proxy and prevents SSRF to Redis/MinIO/etc.
12+
# inline allowlist proxy and prevents SSRF to Redis/S3/etc.
1313
- NET_ADMIN
1414
security_opt:
1515
- apparmor:unconfined
@@ -19,7 +19,7 @@ services:
1919
- .env
2020
environment:
2121
- REDIS_HOST=redis
22-
- MINIO_ENDPOINT=minio:9000
22+
- S3_ENDPOINT=garage:3900
2323
volumes:
2424
- sandbox-data:/var/lib/code-interpreter/sandboxes
2525
# Persistent skill-deps cache: pip/npm/go/cargo install here when
@@ -30,12 +30,13 @@ services:
3030
# to the mounted files inside the container under /app/ssl.
3131
- ${SSL_CERTS_PATH:-./ssl}:/app/ssl:ro
3232
tmpfs:
33+
- /tmp:size=512m,mode=1777,noexec,nosuid,nodev
3334
- /app/data:size=100m
3435
depends_on:
3536
redis:
3637
condition: service_healthy
37-
minio-init:
38-
condition: service_completed_successfully
38+
garage:
39+
condition: service_healthy
3940
healthcheck:
4041
test: ["CMD-SHELL", "curl -fs http://localhost:8000/health || curl -fsk https://localhost:8000/health"]
4142
interval: 30s
@@ -63,43 +64,31 @@ services:
6364
timeout: 5s
6465
retries: 5
6566

66-
minio:
67-
image: minio/minio:latest
68-
container_name: code-interpreter-minio
67+
# Garage S3-compatible object storage (replaces MinIO)
68+
garage:
69+
image: dxflrs/garage:v2.3.0
70+
container_name: code-interpreter-garage
6971
restart: unless-stopped
72+
command: /garage server --single-node --default-bucket
7073
ports:
71-
- "127.0.0.1:${MINIO_PORT:-9000}:9000"
72-
- "127.0.0.1:${MINIO_CONSOLE_PORT:-9001}:9001"
74+
- "127.0.0.1:${S3_PORT:-3900}:3900"
75+
- "127.0.0.1:${GARAGE_ADMIN_PORT:-3903}:3903"
7376
environment:
74-
MINIO_ROOT_USER: ${MINIO_ACCESS_KEY:-minioadmin}
75-
MINIO_ROOT_PASSWORD: ${MINIO_SECRET_KEY:-minioadmin}
76-
command: server /data --console-address ":9001"
77+
GARAGE_DEFAULT_ACCESS_KEY: ${S3_ACCESS_KEY:-GKminioadmin0000}
78+
GARAGE_DEFAULT_SECRET_KEY: ${S3_SECRET_KEY:-minioadminsecret}
79+
GARAGE_DEFAULT_BUCKET: ${S3_BUCKET:-code-interpreter-files}
7780
volumes:
78-
- minio-data:/data
81+
- garage-data:/var/lib/garage
82+
- ./garage.toml:/etc/garage.toml
7983
healthcheck:
80-
test: ["CMD", "curl", "-f", "http://localhost:9000/minio/health/live"]
84+
test: ["CMD", "/garage", "status"]
8185
interval: 10s
8286
timeout: 5s
8387
retries: 5
84-
85-
minio-init:
86-
image: minio/mc:latest
87-
depends_on:
88-
minio:
89-
condition: service_healthy
90-
entrypoint: >
91-
/bin/sh -c "
92-
mc alias set myminio http://minio:9000 $${MINIO_ACCESS_KEY:-minioadmin} $${MINIO_SECRET_KEY:-minioadmin};
93-
mc mb --ignore-existing myminio/$${MINIO_BUCKET:-code-interpreter-files};
94-
exit 0;
95-
"
96-
environment:
97-
MINIO_ACCESS_KEY: ${MINIO_ACCESS_KEY:-minioadmin}
98-
MINIO_SECRET_KEY: ${MINIO_SECRET_KEY:-minioadmin}
99-
MINIO_BUCKET: ${MINIO_BUCKET:-code-interpreter-files}
88+
start_period: 10s
10089

10190
volumes:
10291
sandbox-data:
10392
skill-deps:
10493
redis-data:
105-
minio-data:
94+
garage-data:

docker-compose.yml

Lines changed: 19 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -23,19 +23,20 @@ services:
2323
environment:
2424
# Container-specific overrides (service discovery within compose network)
2525
- REDIS_HOST=redis
26-
- MINIO_ENDPOINT=minio:9000
26+
- S3_ENDPOINT=garage:3900
2727
volumes:
2828
- sandbox-data:/var/lib/code-interpreter/sandboxes
2929
# SSL_CERTS_PATH is a host path; SSL_CERT_FILE and SSL_KEY_FILE must point
3030
# to the mounted files inside the container under /app/ssl.
3131
- ${SSL_CERTS_PATH:-./ssl}:/app/ssl:ro
3232
tmpfs:
33+
- /tmp:size=512m,mode=1777,noexec,nosuid,nodev
3334
- /app/data:size=100m
3435
depends_on:
3536
redis:
3637
condition: service_healthy
37-
minio-init:
38-
condition: service_completed_successfully
38+
garage:
39+
condition: service_healthy
3940
healthcheck:
4041
test: ["CMD-SHELL", "curl -fs http://localhost:8000/health || curl -fsk https://localhost:8000/health"]
4142
interval: 30s
@@ -65,44 +66,30 @@ services:
6566
timeout: 5s
6667
retries: 5
6768

68-
# MinIO for file storage
69-
minio:
70-
image: minio/minio:latest
71-
container_name: code-interpreter-minio
69+
# Garage S3-compatible object storage (replaces MinIO)
70+
garage:
71+
image: dxflrs/garage:v2.3.0
72+
container_name: code-interpreter-garage
7273
restart: unless-stopped
74+
command: /garage server --single-node --default-bucket
7375
ports:
74-
- "127.0.0.1:${MINIO_PORT:-9000}:9000"
75-
- "127.0.0.1:${MINIO_CONSOLE_PORT:-9001}:9001"
76+
- "127.0.0.1:${S3_PORT:-3900}:3900"
77+
- "127.0.0.1:${GARAGE_ADMIN_PORT:-3903}:3903"
7678
environment:
77-
MINIO_ROOT_USER: ${MINIO_ACCESS_KEY:-minioadmin}
78-
MINIO_ROOT_PASSWORD: ${MINIO_SECRET_KEY:-minioadmin}
79-
command: server /data --console-address ":9001"
79+
GARAGE_DEFAULT_ACCESS_KEY: ${S3_ACCESS_KEY:-GKminioadmin0000}
80+
GARAGE_DEFAULT_SECRET_KEY: ${S3_SECRET_KEY:-minioadminsecret}
81+
GARAGE_DEFAULT_BUCKET: ${S3_BUCKET:-code-interpreter-files}
8082
volumes:
81-
- minio-data:/data
83+
- garage-data:/var/lib/garage
84+
- ./garage.toml:/etc/garage.toml
8285
healthcheck:
83-
test: ["CMD", "curl", "-f", "http://localhost:9000/minio/health/live"]
86+
test: ["CMD", "/garage", "status"]
8487
interval: 10s
8588
timeout: 5s
8689
retries: 5
87-
88-
# MinIO bucket initialization
89-
minio-init:
90-
image: minio/mc:latest
91-
depends_on:
92-
minio:
93-
condition: service_healthy
94-
entrypoint: >
95-
/bin/sh -c "
96-
mc alias set myminio http://minio:9000 $${MINIO_ACCESS_KEY:-minioadmin} $${MINIO_SECRET_KEY:-minioadmin};
97-
mc mb --ignore-existing myminio/$${MINIO_BUCKET:-code-interpreter-files};
98-
exit 0;
99-
"
100-
environment:
101-
MINIO_ACCESS_KEY: ${MINIO_ACCESS_KEY:-minioadmin}
102-
MINIO_SECRET_KEY: ${MINIO_SECRET_KEY:-minioadmin}
103-
MINIO_BUCKET: ${MINIO_BUCKET:-code-interpreter-files}
90+
start_period: 10s
10491

10592
volumes:
10693
sandbox-data:
10794
redis-data:
108-
minio-data:
95+
garage-data:

garage.toml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
metadata_dir = "/var/lib/garage/meta"
2+
data_dir = "/var/lib/garage/data"
3+
db_engine = "sqlite"
4+
replication_factor = 1
5+
6+
rpc_bind_addr = "[::]:3901"
7+
rpc_secret = "0000000000000000000000000000000000000000000000000000000000000000"
8+
9+
[s3_api]
10+
s3_region = "garage"
11+
api_bind_addr = "[::]:3900"
12+
13+
[admin]
14+
api_bind_addr = "[::]:3903"

requirements.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@ redis==7.2.0
1616
# SQLite async support for metrics
1717
aiosqlite>=0.19.0
1818

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

2222
# Date/time parsing utilities
2323
python-dateutil==2.9.0.post0

src/api/exec.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ async def execute_code(
6464
within the same session, whether the caller supplies `session_id` directly
6565
or the orchestrator reuses a session through same-user file references or
6666
`entity_id` continuity. State is stored in Redis (2 hour TTL) with
67-
automatic archival to MinIO for long-term storage (7 day TTL).
67+
automatic archival to S3 for long-term storage (configurable TTL).
6868
6969
Returns a streaming response that sends keepalive whitespace before the
7070
JSON body to prevent client socket timeouts during long operations.

src/api/files.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ async def upload_file(
135135
# Sanitize filename to match what will be used in container
136136
sanitized_name = OutputProcessor.sanitize_filename(file.filename)
137137

138-
# Store with sanitized name so MinIO, sandbox, and cleanup all use the same name
138+
# Store with sanitized name so S3, sandbox, and cleanup all use the same name
139139
file_id = await file_service.store_uploaded_file(
140140
session_id=session_id,
141141
filename=sanitized_name,

src/api/health.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -116,25 +116,25 @@ async def redis_health_check(_: str = Depends(verify_api_key)):
116116
)
117117

118118

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

125125
if result.status == HealthStatus.UNHEALTHY:
126126
return JSONResponse(status_code=503, content=result.to_dict())
127127
else:
128128
return JSONResponse(status_code=200, content=result.to_dict())
129129

130130
except Exception as e:
131-
logger.error("MinIO health check failed", error=str(e))
131+
logger.error("S3 health check failed", error=str(e))
132132
return JSONResponse(
133133
status_code=503,
134134
content={
135-
"service": "minio",
135+
"service": "s3",
136136
"status": "unhealthy",
137-
"error": str(e) if settings.api_debug else "MinIO check failed",
137+
"error": str(e) if settings.api_debug else "S3 check failed",
138138
},
139139
)
140140

0 commit comments

Comments
 (0)