Joblet is a micro-container runtime for running Linux jobs with: Process and filesystem isolation (PID namespace, chroot) Fine-grained CPU, memory, and IO throttling (cgroups v2) Secure job execution with mTLS and RBAC Built-in scheduler, SSE log streaming, and multi-core pinning Ideal for: Agentic AI Workloads (Untrusted code)
Joblet is a high-performance job execution system with a monorepo structure containing two main services and a CLI tool.
joblet/
├── cmd/
│ ├── joblet/ # Main service daemon
│ └── rnx/ # CLI client
├── internal/
│ ├── joblet/ # Main service implementation
│ ├── rnx/ # CLI implementation
│ └── proto/ # Internal protocol buffers (IPC, persist)
├── persist/ # Persistence service (sub-module)
│ ├── cmd/persist/
│ ├── internal/
│ └── go.mod # Separate Go module
├── state/ # State persistence service (sub-module)
│ ├── cmd/state/ # State service binary
│ ├── internal/
│ │ ├── storage/ # Backend interface (Memory, DynamoDB, Redis)
│ │ └── ipc/ # IPC server
│ └── go.mod # Separate Go module (AWS SDK dependencies)
├── api/
│ └── gen/ # Generated public API (from external joblet-proto)
├── pkg/ # Shared packages
│ ├── config/ # Configuration management
│ ├── logger/ # Logging utilities
│ ├── security/ # TLS/mTLS support
│ └── client/ # gRPC clients
└── scripts/ # Build and deployment scripts
bin/joblet:50051 (gRPC)bin/persist:50052 (gRPC)bin/state/opt/joblet/run/state-ipc.sock)bin/rnx┌─────────────┐ ┌──────────────────┐
│ RNX Client │◄──── gRPC ────────►│ Joblet Service │
└─────────────┘ :50051 │ (main) │
└──────────────────┘
│
│ Unix Socket
│ IPC
│
▼
┌──────────────────┐
│ Joblet-Persist │
│ (storage) │
└──────────────────┘
│
│ gRPC
▼
┌──────────────────┐
│ RNX Queries │
│ (historical data)│
└──────────────────┘
joblet.proto (external, versioned)github.com/ehsaniara/joblet-protoapi/gen/internal/proto/ipc.proto/opt/joblet/run/persist.sockinternal/proto/persist.protogithub.com/ehsaniara/joblet-protojoblet.protoapi/gen/joblet.pb.go, joblet_grpc.pb.gointernal/proto/ipc.proto - IPC messages (logs, metrics)persist.proto - Persistence service APIinternal/proto/gen/Both services use: /opt/joblet/config/joblet-config.yml
# Main service config (top-level)
server:
port: 50051
joblet:
maxConcurrentJobs: 100
# ... other main config
# Persist service config (nested)
persist:
server:
grpc_socket: "/opt/joblet/run/persist-grpc.sock" # Unix socket for gRPC queries
ipc:
socket: "/opt/joblet/run/persist-ipc.sock" # Unix socket for log/metric writes
# ... other persist config
The persist service automatically detects and reads from the persist: section.
Both services support TLS encryption:
scripts/certs_gen_embedded.shsecurity: sectionpersist.tls sectionpkg/security/tls.goLoadServerTLSConfig() - Server-side TLSLoadClientTLSConfig() - Client-side TLSmake all - Build all binariesmake joblet - Build main service onlymake rnx - Build CLI onlymake persist - Build persist service onlymake proto - Generate all proto filesmake clean - Remove build artifactsmake deploy - Deploy to remote servermake test - Run all testsProto generation uses two approaches:
scripts/generate-proto.sh - External public API
joblet-proto moduleapi/gen/go generate ./internal/proto - Internal protos (IPC and Persist)
internal/proto/ filesinternal/proto/gen/ipc/ and internal/proto/gen/persist///go:generate directives in internal/proto/generate.goDevelopers can regenerate protos with:
make proto # Regenerate all protos
go generate ./internal/proto # Regenerate internal protos only
1. RNX sends RunJob request → Joblet
2. Joblet creates job record in memory + sends to State (async IPC)
3. State persists job metadata to backend (Memory/DynamoDB)
4. Joblet creates isolated environment (cgroups, namespaces)
5. Joblet executes job and streams live logs → RNX
6. Joblet collects metrics (CPU, memory, GPU, I/O)
7. Joblet sends logs/metrics → Persist (via IPC)
8. Persist stores to disk (/opt/joblet/logs, /opt/joblet/metrics)
9. Joblet updates job status + sends to State (async IPC)
1. RNX sends QueryLogs request → Persist
2. Persist reads from disk storage
3. Persist streams results → RNX
1. Joblet job lifecycle events (create, update, complete) → State (async IPC)
2. State writes to backend (Memory/DynamoDB) with 5s timeout
3. On joblet restart: Joblet requests job sync → State
4. State reads from backend → returns all jobs
5. Joblet populates in-memory cache with persisted jobs
/opt/joblet/
├── bin/ # Binaries
│ ├── joblet
│ ├── rnx
│ ├── persist
│ └── state # State persistence service
├── config/ # Configuration
│ └── joblet-config.yml # Unified config (all services)
├── run/ # Runtime files
│ ├── persist-ipc.sock # IPC socket (log/metric writes)
│ ├── persist-grpc.sock # gRPC socket (historical queries)
│ └── state-ipc.sock # IPC socket (job state persistence)
├── jobs/ # Job workspaces
│ └── {job-uuid}/ # Per-job directory
├── logs/ # Historical logs
│ └── {job-uuid}/ # Per-job log files
├── metrics/ # Historical metrics
│ └── {job-uuid}/ # Per-job metric files
├── volumes/ # Named volumes
├── network/ # Network state
└── runtimes/ # Runtime environments
Single systemd service:
/opt/joblet/bin/joblet/opt/joblet/config/joblet-config.ymlmake deploy REMOTE_HOST=192.168.1.161 REMOTE_USER=jay
This builds all binaries, copies to remote server, and restarts services.
# Edit code in internal/, cmd/, or persist/
vim internal/joblet/core/executor.go
# For public API changes - update external joblet-proto repo
# For internal changes - edit internal/proto/*.proto
make proto
make clean
make all
make test
make deploy
github.com/ehsaniara/joblet./go.modgithub.com/ehsaniara/joblet-proto@v1.0.9 (external)github.com/ehsaniara/joblet/persist (local replace)github.com/ehsaniara/joblet/state (local replace)github.com/ehsaniara/joblet/persist./persist/go.modgithub.com/ehsaniara/joblet (local replace to parent)github.com/ehsaniara/joblet/state./state/go.modgithub.com/ehsaniara/joblet (local replace to parent)github.com/aws/aws-sdk-go-v2 (DynamoDB backend)github.com/aws/aws-sdk-go-v2/service/dynamodb (DynamoDB client)pkg/