Summary
Currently, the PinShare deployment runs both the IPFS daemon and the PinShare application in a single container. This violates Kubernetes best practices and causes several operational issues, including permission conflicts, difficult resource management, and poor observability.
This issue proposes refactoring the deployment to use the sidecar pattern, where IPFS runs in a separate container within the same pod.
Current Architecture Problems
1. Single Point of Failure
- If either IPFS or PinShare crashes, the entire container restarts
- No independent process supervision
- Both processes managed via shell backgrounding (
&) which is fragile
2. Permission Conflicts
- IPFS daemon needs to run as the
ipfs user (UID 1000)
- PinShare application has different permission requirements
- Init container creates files as root, causing permission denied errors
- Current issue:
Error: error loading plugins: open /data/ipfs/config: permission denied
3. Resource Management
- Cannot set independent CPU/memory limits for IPFS vs PinShare
- Cannot independently scale IPFS and PinShare
- Resource contention between processes is not visible to Kubernetes scheduler
4. Health Checks & Observability
- Cannot independently monitor IPFS daemon health vs PinShare API health
- Liveness probe only checks IPFS, not PinShare
- Readiness probe only checks PinShare, not IPFS
- Mixed logs from both processes make debugging difficult
5. Update & Maintenance Challenges
- Cannot update IPFS independently from PinShare
- Docker image contains both IPFS (from Kubo) and PinShare binaries
- Larger image size and longer build times
- Difficult to test IPFS and PinShare separately
6. Startup Complexity
The entrypoint.sh script has brittle startup logic:
/usr/local/bin/start_ipfs daemon --migrate=true --agent-version-suffix=docker &
sleep 10 # Hope IPFS starts in 10 seconds
ipfs config ... # Configure CORS
sleep 50 # Wait more just to be safe
/opt/pinshare/bin/pinshare # Finally start PinShare
Hard-coded sleep timers are unreliable and waste time.
Proposed Solution: Sidecar Pattern
Architecture
Pod: pinshare-backend
├── Container: ipfs (sidecar)
│ ├── Image: ipfs/kubo:latest
│ ├── Ports: 5001 (API), 8080 (Gateway), 4001 (Swarm)
│ ├── Volume: ipfs-data (PVC)
│ ├── Health: IPFS dag stat check
│ └── Resources: Independent limits
│
├── Container: pinshare (main)
│ ├── Image: pinshare:latest (simplified, no IPFS)
│ ├── Ports: 9090 (API), 50001 (libp2p)
│ ├── Volume: backend-data (PVC)
│ ├── Health: HTTP /health check
│ ├── Resources: Independent limits
│ └── Connects to: localhost:5001 (IPFS)
│
└── InitContainer: ipfs-config
├── Configures IPFS CORS before startup
└── Sets proper permissions
Benefits
✅ Proper Separation of Concerns
- Each container has a single responsibility
- IPFS manages storage and IPFS protocol
- PinShare manages business logic and P2P metadata
✅ Independent Health Monitoring
# IPFS Container
livenessProbe:
exec:
command: ["ipfs", "dag", "stat", "/ipfs/QmUNLLsPACCz1vLxQVkXqqLX5R1X345qqfHbsf67hvA3Nn"]
# PinShare Container
livenessProbe:
httpGet:
path: /health
port: 9090
✅ Independent Resource Management
# IPFS Container
resources:
requests:
memory: "512Mi"
cpu: "250m"
limits:
memory: "2Gi"
cpu: "1000m"
# PinShare Container
resources:
requests:
memory: "256Mi"
cpu: "100m"
limits:
memory: "1Gi"
cpu: "500m"
✅ Cleaner Logs
kubectl logs pod/pinshare-xxx -c ipfs → Only IPFS logs
kubectl logs pod/pinshare-xxx -c pinshare → Only PinShare logs
- Better for log aggregation systems (ELK, Loki, etc.)
✅ Faster Iteration
- Rebuild only PinShare without touching IPFS
- Use standard
ipfs/kubo image directly (no custom build)
- Smaller PinShare image (no IPFS binaries)
- Faster Tilt hot reload
✅ Easier Testing
- Test IPFS independently
- Test PinShare with mock IPFS
- Integration tests use the same sidecar pattern
Implementation Plan
Phase 1: Refactor Dockerfile
Current: Multi-stage build with Go builder + IPFS layer + Debian
Proposed: Simple Go builder → minimal runtime (no IPFS)
FROM golang:latest AS builder
WORKDIR /build
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN go build -o pinshare .
FROM debian:stable-slim
RUN apt-get update && apt-get install -y \
chromium \
ca-certificates \
clamav clamav-daemon \
&& rm -rf /var/lib/apt/lists/*
COPY --from=builder /build/pinshare /usr/local/bin/pinshare
EXPOSE 9090 50001
CMD ["pinshare"]
Phase 2: Update Kubernetes Manifests
Deployment Changes:
- Add IPFS sidecar container
- Remove IPFS from main container
- Add init container for IPFS CORS configuration
- Update volume mounts (shared data, separate IPFS repo)
- Independent health checks
Service Changes:
- Ensure all necessary ports are exposed
- IPFS API (5001) accessible from PinShare container via localhost
Phase 3: Update Tiltfile
# Simplified live_update for PinShare (no IPFS layers)
docker_build(
'pinshare-backend',
'.',
dockerfile='Dockerfile',
live_update=[
sync('.', '/app'),
run('go build -o /usr/local/bin/pinshare .',
trigger=['./cmd', './internal', './pkg', 'go.mod', 'go.sum']),
],
)
# IPFS uses standard image (no rebuild needed)
Phase 4: Update Documentation
- Update README with new architecture
- Update k8s/README.md with sidecar explanation
- Add architecture diagrams
- Document troubleshooting for each container
Phase 5: Docker Compose (Optional)
Consider updating docker-compose.yml to also use separate services for consistency:
services:
ipfs:
image: ipfs/kubo:latest
volumes:
- ipfs-data:/data/ipfs
ports:
- "5001:5001"
- "8080:8080"
- "4001:4001"
pinshare:
build: .
depends_on:
- ipfs
environment:
- IPFS_API=http://ipfs:5001
ports:
- "9090:9090"
- "50001:50001"
Migration Strategy
Backwards Compatibility
- Keep existing Dockerfile with a
-legacy suffix for rollback
- Create new
Dockerfile with sidecar-compatible build
- Both can coexist during transition
Testing
- Test locally with new Dockerfile
- Test with Tilt in local Kubernetes
- Verify all functionality works (file upload, IPFS pinning, P2P sync)
- Load testing to verify resource limits are appropriate
Rollout
- Update documentation first
- Merge Dockerfile changes
- Update Kubernetes manifests
- Update Tiltfile
- Notify users of new architecture
Acceptance Criteria
Related Issues
- Kubernetes deployment support (depends on #TBD)
- Permission issues with IPFS persistent volumes
References
Summary
Currently, the PinShare deployment runs both the IPFS daemon and the PinShare application in a single container. This violates Kubernetes best practices and causes several operational issues, including permission conflicts, difficult resource management, and poor observability.
This issue proposes refactoring the deployment to use the sidecar pattern, where IPFS runs in a separate container within the same pod.
Current Architecture Problems
1. Single Point of Failure
&) which is fragile2. Permission Conflicts
ipfsuser (UID 1000)Error: error loading plugins: open /data/ipfs/config: permission denied3. Resource Management
4. Health Checks & Observability
5. Update & Maintenance Challenges
6. Startup Complexity
The
entrypoint.shscript has brittle startup logic:Hard-coded sleep timers are unreliable and waste time.
Proposed Solution: Sidecar Pattern
Architecture
Benefits
✅ Proper Separation of Concerns
✅ Independent Health Monitoring
✅ Independent Resource Management
✅ Cleaner Logs
kubectl logs pod/pinshare-xxx -c ipfs→ Only IPFS logskubectl logs pod/pinshare-xxx -c pinshare→ Only PinShare logs✅ Faster Iteration
ipfs/kuboimage directly (no custom build)✅ Easier Testing
Implementation Plan
Phase 1: Refactor Dockerfile
Current: Multi-stage build with Go builder + IPFS layer + Debian
Proposed: Simple Go builder → minimal runtime (no IPFS)
Phase 2: Update Kubernetes Manifests
Deployment Changes:
Service Changes:
Phase 3: Update Tiltfile
Phase 4: Update Documentation
Phase 5: Docker Compose (Optional)
Consider updating docker-compose.yml to also use separate services for consistency:
Migration Strategy
Backwards Compatibility
-legacysuffix for rollbackDockerfilewith sidecar-compatible buildTesting
Rollout
Acceptance Criteria
Related Issues
References