README
Building the Flyte docs locally
Prerequisites
* conda (We recommend Miniconda installed with an official installer)
Set up the build environment
In the flyteorg/flyte root directory do:
$ conda-lock install --name monodocs-env monodocs-environment.lock.yaml
$ conda activate monodocs-env
$ pip install ./flyteidlThis creates a new environment called monodocs-env with all the dependencies needed to build the docs. You can choose a different environment name if you like.
Building the docs
In the flyteorg/flyte root directory make sure you have activated the monodocs-env (or whatever you called it) environment and do:
need to set this to a fake value to build the docs locally
$ export DOCSEARCH_API_KEY=fake-api-key$ make docsThe resulting html files will be in docs/_build/html.
---
BACKEND README
Flyte 2 Backend
This repository contains the backend infrastructure for deploying a distributed, multi-node version of Flyte 2. The backend is Kubernetes-native — it orchestrates workflow execution using Kubernetes primitives, scheduling tasks as pods across clusters with built-in support for multi-cluster routing, service account-based identity, and pod-level log tracking. The core architecture consists of gRPC services (QueueService, RunService, StateService) backed by PostgreSQL, using async processing and real-time streaming via PostgreSQL LISTEN/NOTIFY. See the full Implementation Spec for details.
This repo also defines the protocol buffer schemas for Flyte's APIs and generates client libraries for Go, TypeScript, Python, and Rust. Deploy this when you need Flyte running as a scalable, distributed service across your organization.
Want to contribute? Join us on slack.flyte.org to get involved.
Repository Structure
flyte/
├── flyteidl2/ # Protocol buffer definitions
│ ├── common/ # Common types and utilities
│ ├── core/ # Core Flyte types (tasks, workflows, literals)
│ ├── imagebuilder/ # Image builder service definitions
│ ├── logs/ # Logging types
│ ├── secret/ # Secret management types
│ ├── task/ # Task execution types
│ ├── trigger/ # Trigger service definitions
│ ├── workflow/ # Workflow types
│ └── gen_utils/ # Language-specific generation utilities
├── gen/ # Generated code (not checked into version control)
│ ├── go/ # Generated Go code
│ ├── ts/ # Generated TypeScript code
│ ├── python/ # Generated Python code
│ └── rust/ # Generated Rust code
├── buf.yaml # Buf configuration
├── buf.gen.*.yaml # Language-specific generation configs
└── Makefile # Build automationPrerequisites
- Buf CLI - Protocol buffer tooling
- Go 1.26.5 or later
- Node.js/npm (for TypeScript generation)
- Python 3.9+ with uv package manager (for Python generation)
- Rust toolchain (for Rust generation)
Quick Start
Generate All Code
To generate code for all supported languages:
make genThis will:
1. Update buf dependencies
2. Format and lint proto files
3. Generate code for Go, TypeScript, Python, and Rust
4. Generate mocks for Go
5. Run go mod tidy
Generate for Specific Languages Locally
make buf-go # Generate Go code only
make buf-ts # Generate TypeScript code only
make buf-python # Generate Python code only
make buf-rust # Generate Rust code onlyMaking Changes
1. Modify Protocol Buffers
Edit .proto files in the flyteidl2/ directory following these guidelines:
- Follow the existing naming conventions
- Use proper protobuf style (snake_case for fields, PascalCase for messages)
- Add appropriate comments and documentation
- Ensure backward compatibility when modifying existing messages
2. Generate Code
After modifying proto files:
make docker-pull # Pull the docker image for generation
make gen3. Verify Your Changes
Run the following to ensure everything builds correctly:
For Go
make go-tidy
go build ./...For Rust
make build-crateFor Python
cd gen/python && uv lockFor TypeScript
cd gen/ts && npm install4. Generate Mocks (Go only)
If you've added or modified Go interfaces:
make genDevelopment Workflow
1. Format proto files: make buf-format
2. Lint proto files: make buf-lint
3. Generate code: make buf or make gen
4. Verify builds: Build generated code in your target language
5. Commit changes: Commit both proto files and generated code
Common Tasks
Update Buf Dependencies
make genView Available Commands
make helpVersioning and Releases
See CONTRIBUTING.md for detailed release instructions.
Generated Code
The gen/ directory contains auto-generated code and should not be manually edited. Changes to generated code should be made by:
1. Modifying the source .proto files in flyteidl2/
2. Updating generation utilities in flyteidl2/gen_utils/ if needed
3. Running make gen to regenerate all code
Troubleshooting
Buf Errors
- Ensure you have the latest version of Buf:
buf --version- Update dependencies:
make buf-dep- Check
buf.lock for dependency conflictsGo Module Issues
- Run
make go-tidy to clean up dependencies- Ensure you're using Go 1.26.5 or later
Python Generation Issues
- Ensure
uv is installed: pip install uv- Set the environment variable:
export SETUPTOOLS_SCM_PRETEND_VERSION=0.0.0Rust Build Issues
- Update Rust toolchain:
rustup update- Navigate to
gen/rust and run cargo updateContributing
We welcome contributions to Flyte 2! Please follow the guide here.
---
DOCKER QUICK START
Docker Development - Quick Start
This guide gets you started with Docker-based development in under 5 minutes.
Why Docker?
Using Docker ensures your local environment matches CI exactly, eliminating "works on my machine" issues.
Quick Start
1. Pull the Image
make docker-pullor
docker pull ghcr.io/flyteorg/flyte/ci:v22. Run Common Commands
#### Generate Protocol Buffers
make gen#### Interactive Shell
make docker-shell3. Manual Docker Commands
If you prefer not to use Make:
Generate files
docker run --rm -v $(pwd):/workspace -w /workspace \
ghcr.io/flyteorg/flyte/ci:v2 make genInteractive shell
docker run --rm -it -v $(pwd):/workspace -w /workspace \
ghcr.io/flyteorg/flyte/ci:v2 bashAvailable Make Targets
Run make help to see all available targets including Docker-based ones:
make helpDocker-specific targets:
- make docker-pull - Pull the latest CI image
- make docker-shell - Start interactive shell
- make gen - Run code generation
- make build-crate - Build Rust crate
Troubleshooting
Permission Issues
If generated files have wrong ownership:
docker run --rm -v $(pwd):/workspace -w /workspace \
--user $(id -u):$(id -g) \
ghcr.io/flyteorg/flyte/ci:v2 make genAuthentication Issues
Login to GitHub Container Registry:
gh auth token | docker login ghcr.io -u YOUR_USERNAME --password-stdinUpdating the Docker Image
Fast Local Iteration (Recommended)
If you're modifying gen.Dockerfile, build and test locally first:
One command to build and generate
make docker-devOr step-by-step
make docker-build # Build image
make docker-shell # Test interactively
make gen # Run generationThis is much faster than waiting for PR builds!
PR Testing (After Local Testing)
Once your local changes work:
1. Create a PR with your changes
2. Wait for build - A bot will comment with the PR-specific image tag
3. Test with PR image to verify CI works:
docker pull ghcr.io/flyteorg/flyte/ci:pr-123 # Use your PR number
docker run --rm -it -v $(pwd):/workspace -w /workspace \
ghcr.io/flyteorg/flyte/ci:pr-123 bash4. CI automatically uses your new image in the PR
Workflow Comparison
Local iteration (seconds to minutes):
vim gen.Dockerfile
make docker-dev # Fast!
Repeat until it works
PR iteration (5-10 minutes per build):
git push
Wait for build...
docker pull ghcr.io/flyteorg/flyte/ci:pr-123
Test
Use local iteration first, then validate with PR!
More Information
See docs/docker-image-workflow.md for comprehensive documentation.
---
Docker Image Workflow
Docker Image Build Workflow
This document explains how the Docker CI image is built and used across different scenarios.
Workflow Diagrams
Scenario 1: Regular PR (No Dockerfile Changes)
┌─────────────────────────────────────────────────────────────┐
│ Developer creates PR (no gen.Dockerfile changes) │
└─────────────────┬───────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ check-generate workflow triggers │
│ • Checks if gen.Dockerfile modified: NO │
│ • Uses image: ghcr.io/flyteorg/flyte/ci:v2 │
└─────────────────┬───────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ Pulls existing v2 image and runs checks │
│ ✓ Fast: No image build needed │
└─────────────────────────────────────────────────────────────┘Scenario 2: PR with Dockerfile Changes
/ Detailed source-code truncated for AI context efficiency. /Scenario 3: Merged to v2 Branch
┌─────────────────────────────────────────────────────────────┐
│ PR merged to v2 branch │
└─────────────────┬───────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ build-ci-image workflow triggers on push │
└─────────────────┬───────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ Builds and pushes with tags: │
│ • ghcr.io/flyteorg/flyte/ci:v2 │
│ • ghcr.io/flyteorg/flyte/ci:v2-sha-abc123 │
└─────────────────┬───────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ Future PRs use updated v2 image │
└─────────────────────────────────────────────────────────────┘Image Tag Strategy
| Context | Image Tag | When Created |
|--------------------|----------------------------------------|---------------------------------|
| Regular PR | v2 or latest | Uses existing branch image |
| PR with Dockerfile | pr-123 | Built when PR is created/updated|
| v2 branch push | v2 | Built on every push to v2 |
| main branch push | latest | Built on every push to master |
| Any branch push | {branch}-sha-{commit} | Built on push (with commit SHA) |
Workflow Files
Primary Workflows
1. .github/workflows/build-ci-image.yml
- Builds Docker image
- Runs on: PR with Dockerfile changes, push to main/v2
- Publishes to GHCR with appropriate tags
- Comments on PR with image information
2. .github/workflows/check-generate.yml
- Validates generated files
- Automatically detects if Dockerfile was modified
- Uses PR-specific image if available, otherwise uses v2
3. .github/workflows/regenerate-on-comment.yml
- Regenerates files via /regen comment
- Uses PR-specific image if available
Developer Experience
For Regular Development (No Docker Changes)
Just use the standard v2 image
make docker-pull
make genFor Docker Image Updates
#### Option 1: Local Development (Fastest - Recommended)
Modify gen.Dockerfile
vim gen.DockerfileBuild and test locally
make docker-devIterate quickly
vim gen.Dockerfile
make docker-build # Uses cache, faster rebuilds
make genWhen it works, push to PR
git commit -am "Update Python to 3.13"
git pushBenefits:
- ⚡ Fast iteration (seconds to minutes)
- 🔄 No network dependency
- 🧪 Test before pushing
- 💰 No CI minutes wasted
#### Option 2: PR-Based Testing
1. Modify gen.Dockerfile
vim gen.Dockerfile2. Create PR
git checkout -b update-python
git commit -am "Update Python to 3.13"
git push origin update-python
Create PR on GitHub
3. Wait for build (~5-10 minutes)
Bot will comment: "🐳 Docker CI Image Built"
Image: ghcr.io/flyteorg/flyte/ci:pr-456
4. Test locally with YOUR image
make docker-pull gen DOCKER_CI_IMAGE=ghcr.io/flyteorg/flyte/ci:pr-4565. CI automatically uses the same image!
No need to merge before testing
Benefits of This Approach
1. Test Before Merge: You can fully test Docker image changes before merging
2. True Parity: Local testing uses the exact same image as CI
3. Fast Iteration: No need to merge to test, iterate in the PR
4. Automatic Detection: Workflows automatically detect which image to use
5. Clean Fallback: If Docker isn't modified, uses the stable v2 image
6. Transparent: Bot comments show exactly what image is being used
Implementation Details
Image Detection Logic
Workflows check if gen.Dockerfile or .github/workflows/build-ci-image.yml were modified:
git diff --name-only origin/$BASE_BRANCH...HEAD | \
grep -E '^(Dockerfile\.ci|\.github/workflows/build-ci-image\.yml)$'If modified:
- Use ghcr.io/flyteorg/flyte/ci:pr-{NUMBER}
- Wait for image to be available (with timeout)
If not modified:
- Use ghcr.io/flyteorg/flyte/ci:v2
- Proceed immediately
Wait Strategy
For PRs with Dockerfile changes, workflows intelligently wait for the build:
1. Check for build workflow: Queries GitHub API for build-ci-image.yml runs
2. Find matching run: Looks for run with same commit SHA
3. Wait for completion: Polls every 20 seconds for up to 20 minutes
4. Verify success: Ensures build succeeded before proceeding
5. Pull fresh image: Downloads the newly built image
Benefits:
- Always waits for the actual build to complete (not just image existence)
- Works correctly on subsequent pushes to the same PR
- Provides clear feedback on build status
- Fails fast if build fails
This ensures tests always run with the freshly built image, even when pushing multiple commits to a PR.
Build Performance
The Docker image is optimized for fast builds using several techniques:
Multi-Stage Build Strategy
The Dockerfile uses parallel multi-stage builds to download tools simultaneously:
┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ Go Stage │ │ Node Stage │ │Python Stage │ │ Buf Stage │
│ (parallel) │ │ (parallel) │ │ (parallel) │ │ (parallel) │
└──────┬──────┘ └──────┬──────┘ └──────┬──────┘ └──────┬──────┘
│ │ │ │
└────────────────┴────────────────┴────────────────┘
│
┌──────▼──────┐
│Final Image │
│ (assembly) │
└─────────────┘Benefits:
- 4x parallelization of downloads
- Leverages official Docker images (pre-built, cached)
- Faster than sequential downloads
Caching Strategy
The build uses a multi-layer caching approach:
1. Registry cache (primary): Stored in GHCR, fastest to pull
2. GitHub Actions cache (secondary): Fallback for layers
3. BuildKit inline cache: Metadata in image layers
4. Cache mounts: For package managers (apt, go mod, cargo)
Cache hierarchy:
1. Try buildcache tag (dedicated cache image)
2. Try current PR tag (if exists)
3. Try v2 tag (stable baseline)
4. Try GHA cache
5. Build from scratchPerformance Improvements
| Optimization | Time Saved |
|--------------|------------|
| Multi-stage parallel builds | ~5-8 min |
| Official image copying vs downloads | ~2-3 min |
| Registry cache (vs no cache) | ~10-12 min |
| Cache mounts for packages | ~1-2 min |
| Total potential savings | 15-20 min |
Build times:
- Cold build (no cache): ~15 min
- Warm build (full cache): ~2-3 min
- Incremental build (partial cache): ~5-8 min
Cache Mounts
The Dockerfile uses BuildKit cache mounts for package managers:
APT packages cached
RUN --mount=type=cache,target=/var/cache/aptGo modules cached
RUN --mount=type=cache,target=/root/go/pkg/modCargo packages cached
RUN --mount=type=cache,target=/root/.cargo/registryThese persist across builds, dramatically speeding up package installation.
---
IMPLEMENTATION SPEC
Implementation Specification: Queue, Runs, and State Services
Overview
This document specifies the implementation of three gRPC services using buf connect:
- QueueService - Manages execution queue for actions
- RunService - Manages workflow runs and their lifecycle
- StateService - Manages state persistence for actions
Each service will have a simple implementation backed by PostgreSQL, using pgx for database operations, and leveraging existing flytestdlib packages for database connectivity and configuration management.
Architecture
/ Detailed source-code truncated for AI context efficiency. /Technology Stack
Core Dependencies
- Protocol: buf connect (using generated code from
gen/go/flyteidl2/workflow/workflowconnect/)- Database: PostgreSQL
- Database Driver: pgx/v5 (for raw queries)
- ORM: gorm (for migrations and basic operations)
- Config Management:
github.com/flyteorg/flyte/v2/flytestdlib/config- Database Utils:
github.com/flyteorg/flyte/v2/flytestdlib/database- Logging:
github.com/flyteorg/flyte/v2/flytestdlib/logger- CLI:
github.com/spf13/cobraService Framework
- HTTP server using
net/http- buf connect handlers mounted on HTTP server
- Graceful shutdown support
- Health check endpoints
Service Specifications
1. QueueService
Location: queue/
Proto Definition: flyteidl2/workflow/queue_service.proto
Connect Interface: workflowconnect.QueueServiceHandler
#### RPCs to Implement:
1. EnqueueAction(EnqueueActionRequest) -> EnqueueActionResponse
- Validates request
- Persists action to queue table
- Returns immediately (async processing)
2. AbortQueuedRun(AbortQueuedRunRequest) -> AbortQueuedRunResponse
- Marks all actions in a run as aborted
- Updates abort metadata
3. AbortQueuedAction(AbortQueuedActionRequest) -> AbortQueuedActionResponse
- Marks specific action as aborted
- Updates abort metadata
#### Database Schema:
/ Detailed source-code truncated for AI context efficiency. /#### Configuration:
// queue/config/config.gotype Config struct {
// HTTP server configuration
Server ServerConfig json:"server"
// Database configuration (reuses flytestdlib)
Database database.DbConfig json:"database"
// Queue specific settings
MaxQueueSize int json:"maxQueueSize" pflag:",Maximum number of queued actions"
WorkerCount int json:"workerCount" pflag:",Number of worker goroutines for processing queue"
}
type ServerConfig struct {
Port int json:"port" pflag:",Port to bind the HTTP server"
Host string json:"host" pflag:",Host to bind the HTTP server"
}
---
2. RunService
Location: runs/
Proto Definition: flyteidl2/workflow/run_service.proto
Connect Interface: workflowconnect.RunServiceHandler
#### RPCs to Implement:
1. CreateRun(CreateRunRequest) -> CreateRunResponse
- Creates a new run record
- Initializes root action
- Returns run metadata
2. AbortRun(AbortRunRequest) -> AbortRunResponse
- Marks run as aborted
- Cascades to all actions
3. GetRunDetails(GetRunDetailsRequest) -> GetRunDetailsResponse
- Fetches complete run information
- Includes root action details
4. WatchRunDetails(WatchRunDetailsRequest) -> stream WatchRunDetailsResponse
- Streams run updates
- Uses PostgreSQL LISTEN/NOTIFY for real-time updates
5. GetActionDetails(GetActionDetailsRequest) -> GetActionDetailsResponse
- Fetches detailed action information
- Includes all attempts
6. WatchActionDetails(WatchActionDetailsRequest) -> stream WatchActionDetailsResponse
- Streams action updates
7. GetActionData(GetActionDataRequest) -> GetActionDataResponse
- Returns input/output references
- Does NOT load actual data (just URIs)
8. ListRuns(ListRunsRequest) -> ListRunsResponse
- Paginated run listing
- Supports filtering by org/project/trigger
9. WatchRuns(WatchRunsRequest) -> stream WatchRunsResponse
- Streams run updates matching filter
10. ListActions(ListActionsRequest) -> ListActionsResponse
- Lists actions for a run
- Paginated
11. WatchActions(WatchActionsRequest) -> stream WatchActionsResponse
- Streams action updates for a run
- Supports filtering
12. WatchClusterEvents(WatchClusterEventsRequest) -> stream WatchClusterEventsResponse
- Streams cluster events for an action attempt
13. AbortAction(AbortActionRequest) -> AbortActionResponse
- Aborts a specific action
#### Database Schema:
/ Detailed source-code truncated for AI context efficiency. /#### Configuration:
// runs/config/config.gotype Config struct {
// HTTP server configuration
Server ServerConfig json:"server"
// Database configuration
Database database.DbConfig json:"database"
// Watch/streaming settings
WatchBufferSize int json:"watchBufferSize" pflag:",Buffer size for watch streams"
}
type ServerConfig struct {
Port int json:"port" pflag:",Port to bind the HTTP server"
Host string json:"host" pflag:",Host to bind the HTTP server"
}
---
3. StateService
Location: state/
Proto Definition: flyteidl2/workflow/state_service.proto
Connect Interface: workflowconnect.StateServiceHandler
#### RPCs to Implement:
1. Put(stream PutRequest) -> stream PutResponse
- Bidirectional streaming
- Persists action state (NodeStatus JSON)
- Returns status for each request
2. Get(stream GetRequest) -> stream GetResponse
- Bidirectional streaming
- Retrieves action state
3. Watch(WatchRequest) -> stream WatchResponse
- Server streaming
- Watches state changes for child actions
- Uses PostgreSQL LISTEN/NOTIFY
#### Database Schema:
/ Detailed source-code truncated for AI context efficiency. /#### Configuration:
// state/config/config.gotype Config struct {
// HTTP server configuration
Server ServerConfig json:"server"
// Database configuration
Database database.DbConfig json:"database"
// State specific settings
MaxStateSizeBytes int json:"maxStateSizeBytes" pflag:",Maximum size of state JSON in bytes"
}
type ServerConfig struct {
Port int json:"port" pflag:",Port to bind the HTTP server"
Host string json:"host" pflag:",Host to bind the HTTP server"
}
---
Unified Binary
Location: cmd/flyte-services/main.go
The unified binary provides a single entrypoint that can run:
1. queue - QueueService only
2. runs - RunService only
3. state - StateService only
4. executor - Kubernetes controller only
5. all - All services together on different ports
Command Structure:
Run queue service only
flyte-services queue --config config.yamlRun runs service only
flyte-services runs --config config.yamlRun state service only
flyte-services state --config config.yamlRun executor only
flyte-services executor --config config.yamlRun all services
flyte-services all --config config.yamlImplementation:
/ Detailed source-code truncated for AI context efficiency. /---
Database Connection Management
All services use flytestdlib for database management:
// Example from queue/service/queue_service.goimport (
"github.com/flyteorg/flyte/v2/flytestdlib/database"
"gorm.io/gorm"
)
func initDB(ctx context.Context, cfg database.DbConfig) (gorm.DB, error) {
gormConfig := &gorm.Config{
// Configuration options
}
// Create database if it doesn't exist
db, err := database.CreatePostgresDbIfNotExists(ctx, gormConfig, cfg.Postgres)
if err != nil {
return nil, err
}
// Apply connection pool settings
sqlDB, err := db.DB()
if err != nil {
return nil, err
}
sqlDB.SetMaxIdleConns(cfg.MaxIdleConnections)
sqlDB.SetMaxOpenConns(cfg.MaxOpenConnections)
sqlDB.SetConnMaxLifetime(cfg.ConnMaxLifeTime.Duration)
return db, nil
}
For pgx-specific operations (like LISTEN/NOTIFY for streaming):
import (
"github.com/jackc/pgx/v5/pgxpool"
)func initPgxPool(ctx context.Context, cfg database.PostgresConfig) (pgxpool.Pool, error) {
connString := fmt.Sprintf(
"postgres://%s:%s@%s:%d/%s?%s",
cfg.User,
resolvePassword(ctx, cfg.Password, cfg.PasswordPath),
cfg.Host,
cfg.Port,
cfg.DbName,
cfg.ExtraOptions,
)
return pgxpool.New(ctx, connString)
}
---
Configuration Management
All services use flytestdlib config:
// Example from queue/cmd/main.goimport (
"github.com/flyteorg/flyte/v2/flytestdlib/config"
queueconfig "github.com/flyteorg/flyte/v2/queue/config"
)
var (
configSection = config.MustRegisterSection("queue", &queueconfig.Config{})
)
func main() {
// Parse config from file and flags
if err := config.LoadConfig(...); err != nil {
panic(err)
}
cfg := configSection.GetConfig().(*queueconfig.Config)
// Use cfg...
}
---
Service Implementation Pattern
Each service follows this pattern:
// queue/service/queue_service.gopackage service
import (
"context"
"connectrpc.com/connect"
"github.com/flyteorg/flyte/v2/gen/go/flyteidl2/workflow"
"github.com/flyteorg/flyte/v2/gen/go/flyteidl2/workflow/workflowconnect"
"github.com/flyteorg/flyte/v2/queue/repository"
)
type QueueService struct {
repo repository.Repository
}
func NewQueueService(repo repository.Repository) *QueueService {
return &QueueService{repo: repo}
}
// Ensure we implement the interface
var _ workflowconnect.QueueServiceHandler = (*QueueService)(nil)
func (s *QueueService) EnqueueAction(
ctx context.Context,
req *connect.Request[workflow.EnqueueActionRequest],
) (*connect.Response[workflow.EnqueueActionResponse], error) {
// Validate request
if err := req.Msg.Validate(); err != nil {
return nil, connect.NewError(connect.CodeInvalidArgument, err)
}
// Persist to database
if err := s.repo.EnqueueAction(ctx, req.Msg); err != nil {
return nil, connect.NewError(connect.CodeInternal, err)
}
return connect.NewResponse(&workflow.EnqueueActionResponse{}), nil
}
// ... other methods
---
HTTP Server Setup
Each service's main.go sets up an HTTP server:
// queue/cmd/main.gopackage cmd
import (
"context"
"fmt"
"net/http"
"github.com/flyteorg/flyte/v2/gen/go/flyteidl2/workflow/workflowconnect"
"github.com/flyteorg/flyte/v2/queue/service"
"github.com/flyteorg/flyte/v2/queue/repository"
)
func RunQueue(ctx context.Context) error {
// Initialize database
db, err := initDB(ctx)
if err != nil {
return err
}
// Run migrations
if err := runMigrations(db); err != nil {
return err
}
// Create repository
repo := repository.NewPostgresRepository(db)
// Create service
svc := service.NewQueueService(repo)
// Create HTTP handler
mux := http.NewServeMux()
path, handler := workflowconnect.NewQueueServiceHandler(svc)
mux.Handle(path, handler)
// Add health check
mux.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
w.Write([]byte("OK"))
})
// Setup HTTP/2 support
addr := fmt.Sprintf("%s:%d", cfg.Server.Host, cfg.Server.Port)
protocols := &http.Protocols{}
protocols.SetHTTP1(true)
protocols.SetUnencryptedHTTP2(true)
server := &http.Server{
Addr: addr,
Handler: mux,
Protocols: protocols,
}
logger.Infof(ctx, "Starting Queue Service on %s", addr)
return server.ListenAndServe()
}
---
Migration Management
Each service uses golang-migrate or similar:
// queue/repository/migrations.gofunc RunMigrations(db *gorm.DB) error {
return db.AutoMigrate(
&models.QueuedAction{},
)
}
Or use raw SQL migrations with a migration tool.
---
Repository Pattern
Each service implements a repository interface:
// queue/repository/interfaces.gotype Repository interface {
EnqueueAction(ctx context.Context, req *workflow.EnqueueActionRequest) error
AbortQueuedRun(ctx context.Context, runID *common.RunIdentifier, reason string) error
AbortQueuedAction(ctx context.Context, actionID *common.ActionIdentifier, reason string) error
GetQueuedActions(ctx context.Context, limit int) ([]*models.QueuedAction, error)
}
// queue/repository/postgres.gotype PostgresRepository struct {
db *gorm.DB
}
func NewPostgresRepository(db *gorm.DB) Repository {
return &PostgresRepository{db: db}
}
func (r PostgresRepository) EnqueueAction(ctx context.Context, req workflow.EnqueueActionRequest) error {
action := &models.QueuedAction{
Org: req.ActionId.RunId.Org,
Project: req.ActionId.RunId.Project,
Domain: req.ActionId.RunId.Domain,
RunName: req.ActionId.RunId.Name,
ActionName: req.ActionId.Name,
ParentActionName: req.ParentActionName,
ActionGroup: req.Group,
Subject: req.Subject,
ActionSpec: req, // Will be marshaled to JSONB
InputUri: req.InputUri,
RunOutputBase: req.RunOutputBase,
Status: "queued",
}
return r.db.WithContext(ctx).Create(action).Error
}
---
Streaming Implementation (Watch/Listen)
For streaming RPCs, use PostgreSQL LISTEN/NOTIFY:
// runs/service/run_service.gofunc (s *RunService) WatchRunDetails(
ctx context.Context,
req *connect.Request[workflow.WatchRunDetailsRequest],
stream *connect.ServerStream[workflow.WatchRunDetailsResponse],
) error {
// Get initial state
details, err := s.repo.GetRunDetails(ctx, req.Msg.RunId)
if err != nil {
return err
}
if err := stream.Send(&workflow.WatchRunDetailsResponse{Details: details}); err != nil {
return err
}
// Subscribe to updates via PostgreSQL LISTEN
updates := make(chan *workflow.RunDetails)
errs := make(chan error)
go s.repo.WatchRunDetails(ctx, req.Msg.RunId, updates, errs)
for {
select {
case <-ctx.Done():
return nil
case err := <-errs:
return err
case details := <-updates:
if err := stream.Send(&workflow.WatchRunDetailsResponse{Details: details}); err != nil {
return err
}
}
}
}
// runs/repository/postgres.gofunc (r *PostgresRepository) WatchRunDetails(
ctx context.Context,
runID *common.RunIdentifier,
updates chan<- *workflow.RunDetails,
errs chan<- error,
) {
conn, err := r.pgxPool.Acquire(ctx)
if err != nil {
errs <- err
return
}
defer conn.Release()
// Listen for notifications
_, err = conn.Exec(ctx, "LISTEN run_updates")
if err != nil {
errs <- err
return
}
for {
notification, err := conn.Conn().WaitForNotification(ctx)
if err != nil {
errs <- err
return
}
// Fetch updated run details
details, err := r.GetRunDetails(ctx, runID)
if err != nil {
errs <- err
return
}
select {
case updates <- details:
case <-ctx.Done():
return
}
}
}
---
Testing Strategy
Unit Tests
- Repository layer: Mock database using testcontainers with PostgreSQL
- Service layer: Mock repository interface
- Use table-driven tests
Integration Tests
- End-to-end tests with real PostgreSQL
- Use docker-compose for local testing
- Test streaming with multiple concurrent clients
Example:
// queue/service/queue_service_test.gofunc TestEnqueueAction(t *testing.T) {
mockRepo := &mocks.Repository{}
svc := service.NewQueueService(mockRepo)
req := connect.NewRequest(&workflow.EnqueueActionRequest{
// ... populate request
})
mockRepo.On("EnqueueAction", mock.Anything, req.Msg).Return(nil)
resp, err := svc.EnqueueAction(context.Background(), req)
assert.NoError(t, err)
assert.NotNil(t, resp)
}
---
Deployment Considerations
Configuration Files
Example config.yaml:
database:
postgres:
host: postgres.flyte.svc.cluster.local
port: 5432
dbname: flyte_queue
username: flyte
passwordPath: /etc/secrets/db-password
extraOptions: "sslmode=require"
maxIdleConnections: 10
maxOpenConnections: 100
connMaxLifeTime: 1hqueue:
server:
host: 0.0.0.0
port: 8089
maxQueueSize: 10000
workerCount: 10
runs:
server:
host: 0.0.0.0
port: 8090
watchBufferSize: 100
state:
server:
host: 0.0.0.0
port: 8091
maxStateSizeBytes: 1048576 # 1MB
Docker Compose (for local development)
version: '3.8'
services:
postgres:
image: postgres:15
environment:
POSTGRES_USER: flyte
POSTGRES_PASSWORD: flyte
POSTGRES_DB: flyte
ports:
- "5432:5432" queue:
build: .
command: queue --config /etc/flyte/config.yaml
ports:
- "8089:8089"
depends_on:
- postgres
runs:
build: .
command: runs --config /etc/flyte/config.yaml
ports:
- "8090:8090"
depends_on:
- postgres
state:
build: .
command: state --config /etc/flyte/config.yaml
ports:
- "8091:8091"
depends_on:
- postgres
---
Implementation Phases
Phase 1: Core Infrastructure
1. Setup project structure
2. Implement database schemas and migrations
3. Implement repository interfaces and PostgreSQL implementations
4. Setup configuration management using flytestdlib
Phase 2: Service Implementation
1. Implement QueueService
2. Implement RunService (non-streaming RPCs first)
3. Implement StateService (non-streaming RPCs first)
Phase 3: Streaming Support
1. Add PostgreSQL LISTEN/NOTIFY support
2. Implement streaming RPCs (Watch*)
3. Test concurrent streaming clients
Phase 4: Integration
1. Implement unified binary command structure
2. Add health checks and metrics
3. Integration testing
4. Documentation
Phase 5: Production Readiness
1. Add observability (metrics, tracing, logging)
2. Performance testing and optimization
3. Security audit
4. Deployment documentation
---
Open Questions
1. Migration Strategy: Should we use golang-migrate, gorm AutoMigrate, or custom SQL scripts?
2. Protobuf Serialization: Store protobuf as JSONB or use binary serialization?
3. Queue Processing: Should QueueService also include worker implementation for processing queued actions?
4. Multi-tenancy: How to handle org/project isolation at the database level?
5. Metrics: What metrics should each service expose?
6. Rate Limiting: Should services implement rate limiting per org/project?
---
References
- Protocol Buffers: queue_service.proto, run_service.proto, state_service.proto
- Generated Code: gen/go/flyteidl2/workflow/workflowconnect/
- Database Utils: flytestdlib/database/
- Config Management: flytestdlib/config/
- Buf Connect: https://connectrpc.com/docs/go/getting-started
- PostgreSQL LISTEN/NOTIFY: https://www.postgresql.org/docs/current/sql-notify.html
---
CONTRIBUTING
Contributing to Flyte
First off, thanks for taking the time to contribute! ❤️
All types of contributions are encouraged and valued. See the Table of Contents for different ways to help and details about how this project handles them. Please make sure to read the relevant section before making your contribution. It will make it a lot easier for the team and smooth out the experience for all involved. The community looks forward to your contributions. 🎉
If you don't have time to contribute, that's fine. There are other easy ways to support the project and show your appreciation, which we would also be very happy about:
- Star the project
- Post on X or Linkedin about Flyte #flyte
- Mention the project at local meetups and tell your friends/colleagues
Table of Contents
- Code of Conduct
- I Have a Question
- I Want To Contribute
- Code Contributors
- Recommendation Order (For Beginners)
- Reporting Bugs
- Feature Requests
- Improving The Documentation
- Improving The Design
- Publish a Blog Post or Case Study
- Commit Messages
- Pull requests
- Contributor Ladder
Code of Conduct
This project and everyone participating in it is governed by the Code of Conduct. By participating, you are expected to uphold this code. Please report unacceptable behavior
to [email protected].
I Have a Question
If you need clarification after reading this document, we encourage you to join our slack workspace and join channels #flyte-support and #contribute channel.
I Want To Contribute
Code Contributors
We’re excited that you’re interested in contributing code to Flyte! Before you start, please take a look at our Getting started docs, it includes setup instructions, build steps, and details on running your first workflow locally.
#### Recommendation Order (For Beginners)
- Finish reading Core Concepts
- Finish reading Connecting a Cluster
- Finish reading Projects and Domains
Reporting Bugs
Please use our issues templates that provide hints on what information we need to help you.
You must never report security related issues, vulnerabilities or bugs including sensitive information to the issue tracker, or elsewhere in public. Instead sensitive bugs must be sent by email to [email protected] or use the Report a security vulnerability issue template.
Feature Requests
Suggest an idea for the project by using the Issues template and choose your desired option and kindly provide as much context as you can about what you're running into. Do not open issues for questions or support, instead join our slack workspace and ask there.
Unsure if your contribution is “small”, “large” or whether it fits into the project's goal? Kindly start a quick discussion on github.
Improving The Documentation
If you notice outdated information or areas that could be clarified, kindly start a discussion in the contribute channel on slack. For more information, please checkout Contributing to documentation.
Improving The Design
Design contributions are welcome! To ensure smooth collaboration, please use the UI Feature Request Template when opening a design-related issue. This helps us gather the right context (such as wireframes, mockups, or visual references) and maintain a consistent design language across the project. Feedback and iterations are highly encouraged, design is always a shared process.
Publish a Blog Post or Case Study
We love hearing how people use or extend Flyte in their own projects. If you’ve written about your experience, we’re happy to review it! To share your work, please start a discussion in the #contribute channel on slack, summarizing your post or case study, with a link to the full content.
Commit Messages
Writing clear and consistent commit messages helps maintainers understand the purpose of your changes. A good commit message should:
- Be written in the present tense (e.g., Add new feature, not Added new feature)
- Be short (50 characters or less for the summary line)
- Include additional context in the body if needed
- Reference related issue numbers (e.g., Fixes #123)
- Keep each commit focused on one logical change
Pull Requests
When you’re ready to contribute your changes, follow these steps to create a clear and reviewable pull request:
- Push your changes to your fork:
git push origin your-branch-name- Open a Pull Request against the main Flyte Code repository.
- Select "Compare across forks" and choose your fork and branch.
- Fill out the PR template with:
- A clear description of your changes
- Any related issues (e.g., “changed `#123”).
- Testing steps or screenshots (if applicable)
- Notes for reviewers, if special attention is needed.
For more context, kindly read the official Before Submitting Your PR docs.
Contributor Ladder
The Flyte Contributor Ladder is a key resource for understanding how to grow within Flyte, outlining expectations, responsibilities, and progression across roles. It helps create transparency, recognize contributions, and ensure a sustainable and inclusive path for community members to deepen their impact. Read more.
Your contributions, big or small help make Flyte better for everyone!🫶
References
This document was adapted from https://contributing.md!
---
README
## Flyte 2 is now generally available!
> Read more in the announcement here.
> Want to try Flyte 2 locally? Run the Devbox.
> Looking for Flyte 1? Go to the master branch, where Flyte 1 is now maintained.
---
Flyte 2
Reliably orchestrate ML pipelines, models, and agents at scale — in pure Python.
[](https://pypi.org/project/flyte/)
[](https://pypi.org/project/flyte/)
[](LICENSE)
[](https://flyte2intro.apps.demo.hosted.unionai.cloud/)
[](https://www.union.ai/docs/v2/flyte/user-guide/running-locally/)
[](https://www.union.ai/docs/v2/byoc/api-reference/flyte-sdk/)
[](https://www.union.ai/docs/v2/byoc/api-reference/flyte-cli/)
Flyte is a Graduated project of the LF AI & Data Foundation.
<a href="https://lfaidata.foundation/projects/flyte/">
<img src="https://raw.githubusercontent.com/flyteorg/static-resources/main/flyte/readme/flyte_and_lf.png" alt="Flyte and LF AI & Data Logo" width="250">
</a>
Install
uv pip install flyteFor the full SDK and development tools, see the flyte-sdk repository.
Example
import asyncio
import flyteenv = flyte.TaskEnvironment(
name="hello_world",
image=flyte.Image.from_debian_base(python_version=(3, 12)),
)
@env.task
def calculate(x: int) -> int:
return x * 2 + 5
@env.task
async def main(numbers: list[int]) -> float:
results = await asyncio.gather(*[
calculate.aio(num) for num in numbers
])
return sum(results) / len(results)
if __name__ == "__main__":
flyte.init()
run = flyte.run(main, numbers=list(range(10)))
print(f"Result: {run.result}")
<table>
<tr><td><b>Python</b></td><td><b>Flyte CLI</b></td></tr>
<tr>
<td>
python hello.py</td>
<td>
flyte run hello.py main --numbers '[1,2,3]'</td>
</tr>
</table>
Serve a Model
serving.py
from fastapi import FastAPI
import flyte
from flyte.app.extras import FastAPIAppEnvironmentapp = FastAPI()
env = FastAPIAppEnvironment(
name="my-model",
app=app,
image=flyte.Image.from_debian_base(python_version=(3, 12)).with_pip_packages(
"fastapi", "uvicorn"
),
)
@app.get("/predict")
async def predict(x: float) -> dict:
return {"result": x * 2 + 5}
if __name__ == "__main__":
flyte.init_from_config()
flyte.serve(env)
<table>
<tr><td><b>Python</b></td><td><b>Flyte CLI</b></td></tr>
<tr>
<td>
python serving.py</td>
<td>
flyte serve serving.py env</td>
</tr>
</table>
Local Development Experience
Install the TUI for a rich local development experience:
uv pip install flyte[tui][](https://www.youtube.com/watch?v=lsfy-7DbbRM)
Try the hosted demo in your browser — no installation required.
Open Source Backend
The open source backend for Flyte 2 is coming soon. This repository will contain the Kubernetes-native backend infrastructure for deploying Flyte 2 as a distributed, multi-node service. See the Backend README for the current state of the backend, protocol buffer definitions, and contribution guide.
If you need an enterprise-ready, production-grade backend for Flyte 2 today, it is available on Union.ai.
Learn More
- Try DevBox - Get started
- SDK Reference — API reference docs
- CLI Reference — CLI docs
- flyte-sdk — The Flyte 2 Python SDK repository
- Slack | GitHub Discussions | Issues
Contributing
We welcome contributions! See the Backend README for backend development, or join us on slack.flyte.org.
Sponsors
CI container image builds for this repository are sponsored by Depot — fast, native multi-arch Docker builds with persistent layer caching.
[](https://depot.dev)
License
Apache 2.0 — see LICENSE.
---