py-serverless-kit
Write your Python functions once. Deploy them to any of 10 clouds by changing one line of YAML.
py-serverless-kit (CLI: pskit) is a Serverless-Framework-style toolkit built on Terraform. Your handlers use one small, provider-neutral API (Event, Context, Response). A pskit.yml file says which cloud they run on and what triggers them: HTTP, schedules, queues, storage, topics. The same file can also define workflows in the style of Step Functions. pskit deploy generates Terraform for the chosen cloud, applies it, and builds an HTML dashboard of everything you deployed.
# app/handlers.py - identical on AWS, Azure, GCP, Alibaba, Oracle, IBM, DigitalOcean, Tencent, Huawei, Cloudflare
from py_serverless_kit import Event, Context, NotFound
def get_order(event: Event, ctx: Context):
order = ORDERS.get(event.http.path_params["id"])
if order is None:
raise NotFound("no such order") # -> HTTP 404 JSON on every cloud
return order # -> HTTP 200 JSON
# pskit.yml
service: orders
provider:
name: aws # <- change to gcp, azure, alibaba, ... and redeploy. That's the migration.
region: us-east-1
functions:
get-order:
handler: app.handlers.get_order
events:
- http: GET /orders/{id}
pskit deploy # terraform apply on AWS
pskit deploy --provider gcp # same code, Google Cloud Functions
pskit dashboard --open # every function, trigger, URL and resource id
Why
Teams that build on AWS Lambda and Step Functions end up with code full of API Gateway event parsing, SQS record loops and hand-written ASL. When the business needs another cloud, every function has to be rewritten. py-serverless-kit fixes this at both layers:
| Layer | Locked-in way | With pskit |
|---|---|---|
| Handler code | Parses event["requestContext"]["http"]["method"], Records[].body, ... |
Uses event.http.method, event.records[].body: the same on every cloud |
| Triggers / infra | SAM / CloudFormation / console clicks | One pskit.yml, rendered to Terraform for the target cloud |
| Workflows | Step Functions ASL | Portable state machine, compiled to Step Functions, Google Cloud Workflows, or run by the built-in engine everywhere else |
| Visibility | One console per cloud | One generated dashboard across all clouds and stages |
Features
- 10 providers: AWS Lambda, Azure Functions, Google Cloud Functions (gen2), Alibaba Function Compute, Oracle Functions, IBM Code Engine, DigitalOcean Functions, Tencent SCF, Huawei FunctionGraph, Cloudflare Workers.
- Triggers: HTTP (path params, CORS, auth), schedules (rate/cron, translated into each cloud's cron dialect), queues, object storage, pub/sub topics, and direct function-to-function
invoke(). - Workflows: task / choice / wait / pass / parallel / map / succeed / fail with retry and catch. Native on AWS and GCP; an embedded engine on the other clouds; runnable locally with
pskit workflow run. - Terraform underneath: real, reviewable
main.tf.json, any backend (S3, GCS, azurerm, ...), works with OpenTofu. Escape hatches:terraform.extra,${tf:...}references, per-functionoptions. - Config that scales:
extends:, per-providerproviders:blocks, per-stagestages:blocks, and${env:},${opt:},${self:},${file()}variables. - Local dev:
pskit dev(hot-reloading HTTP server),pskit invokewith synthetic queue/storage/schedule events, and aLocalRunnerfor pytest. - Dashboard: a self-contained
index.htmllisting every deployment, function, trigger, endpoint, ARN/id, console link and workflow. - Production details: deterministic artifacts (no redeploy when nothing changed), partial batch failures (
BatchResult), structured JSON logs, consistent HTTP error mapping, stack traces hidden from clients, a stdlib-only runtime (no extra dependencies inside your function), and hooks (before_deploy, ...).
Supported providers
| Provider | Tier | HTTP | Schedule | Queue | Storage | Topic | Workflows |
|---|---|---|---|---|---|---|---|
| AWS Lambda | stable | ✅ | ✅ | ✅ SQS | ✅ S3 | ✅ SNS | native (Step Functions) |
| Google Cloud Functions | stable | ✅ | ✅ | ✅ Pub/Sub | ✅ GCS | ✅ Pub/Sub | native (Cloud Workflows) |
| Azure Functions | stable | ✅ | ✅ | ✅ Storage Queue | ✅ Blob | ✅ Service Bus | embedded |
| Alibaba Function Compute | beta | ✅ | ✅ | – | ✅ OSS | ✅ MNS | embedded |
| Huawei FunctionGraph | beta | ✅ | ✅ | – | ✅ OBS | ✅ SMN | embedded |
| Tencent SCF | beta | ✅ | ✅ | – | ✅ COS | – | embedded |
| DigitalOcean Functions | beta | ✅ | ✅ | – | – | – | embedded |
| IBM Code Engine | beta | ✅ | – | – | – | – | embedded |
| Oracle Cloud Functions | experimental | ✅ | – | – | ✅ | – | embedded |
| Cloudflare Workers (Python) | experimental | ✅ | ✅ | – | – | – | embedded |
Run pskit providers for the live matrix. If a config uses a trigger the target cloud can't support, pskit validate fails with a clear message instead of deploying something half-working.
Maturity, honestly stated: the Terraform generated for every provider is checked with
terraform validateagainst the real provider plugins (in CI, too). The stable providers cover the most surface area. beta and experimental providers follow vendor docs for event payloads thatvalidatecan't check, so try them on a non-production account first. See docs/providers.md for each provider's details and limits.
Install
pip install py-serverless-kit
You also need Terraform ≥ 1.5 (or OpenTofu) on your PATH, plus credentials for your cloud. See Getting started.
Quick start
pskit init my-api --provider aws --template full # templates: http | events | workflow | full
cd my-api
pskit dev # http://127.0.0.1:3000/hello
pskit invoke -f hello --path "/hello?name=Ada"
pskit workflow run order-pipeline -i '{"amount": 42}'
pskit deploy --stage dev
pskit info
pskit dashboard --open
pskit remove --stage dev
Move to another cloud
Change provider.name, or keep one file with per-cloud blocks:
provider:
name: aws
region: us-east-1
providers: # applied only when that provider is selected
gcp: {region: europe-west1, options: {project: my-project}}
azure: {region: westeurope, options: {subscription_id: "${env:ARM_SUBSCRIPTION_ID}"}}
pskit deploy --provider gcp
pskit deploy --provider azure --stage prod
Triggers at a glance
functions:
api:
handler: app.api.handle
events:
- http: {method: POST, path: /orders, cors: true}
- http: GET /orders/{id}
nightly:
handler: app.jobs.nightly
events:
- schedule: {cron: "0 2 * * *", input: {report: daily}, timezone: Europe/Berlin}
worker:
handler: app.jobs.process
timeout: 60
events:
- queue: {name: orders, batch_size: 10, dead_letter: orders-dlq}
thumbnails:
handler: app.media.on_upload
events:
- storage: {bucket: my-uploads-bucket, events: [created], suffix: .jpg}
audit:
handler: app.audit.on_event
events:
- topic: order-events
from py_serverless_kit import BatchResult, Event, Context
def process(event: Event, ctx: Context):
result = BatchResult() # retry only failed messages where the cloud supports it
for record in event.records:
try:
handle(record.json())
except Exception:
result.fail(record)
return result
Workflows (portable Step Functions)
workflows:
order-pipeline:
start_at: Validate
states:
Validate:
type: task
function: validate-order
retry: [{errors: [TimeoutError], max_attempts: 3, backoff_rate: 2}]
catch: [{errors: [ValueError], next: Rejected}]
next: Route
Route:
type: choice
choices: [{variable: "$.amount", gt: 1000, next: ManualReview}]
default: Charge
ManualReview: {type: wait, seconds: 3600, next: Charge}
Charge: {type: task, function: charge-order, end: true}
Rejected: {type: fail, error: OrderRejected}
The same definition becomes AWS Step Functions on AWS, Google Cloud Workflows on GCP, and an embedded orchestrator function on the other clouds. pskit workflow show order-pipeline --format asl prints the generated ASL.
How it works
pskit.yml ──► config loader ──► provider validation ──► packager ──────────► artifact.zip
(extends, providers:, (capabilities, (your code + stdlib-only
stages:, ${vars}) limits, runtimes) runtime + manifest + shim)
│
▼
provider renderer ──► .pskit/<provider>-<stage>/main.tf.json
│ terraform init / apply
▼
deployment.json ──► dashboard/index.html
At runtime: cloud event ──► shim ──► runtime.adapters.<cloud> ──► Event ──► your handler
your result ──► Response / raw value ──► adapter ──► native cloud response
More: docs/architecture.md.
Examples
Fifteen runnable scenarios live in examples/. They are tested in CI.
| 01-hello-http | Minimal HTTP function |
| 02-rest-api | CRUD, path params, validation errors, CORS, cookies, binary, JWT |
| 03-scheduled-jobs | rate/cron schedules with input and time zones |
| 04-queue-worker | Queue consumer, partial batch failures, dead-letter queue |
| 05-storage-events | Object created/deleted with filters |
| 06-pubsub-topic | Topic fan-out and filters |
| 07-workflow-orders | Every workflow state type, retry/catch |
| 08-multi-cloud | Same code, all 10 providers |
| 09-function-invoke | Function-to-function invoke() |
| 10-stages-and-config | Stages, variables, secrets |
| 11-custom-terraform | terraform.extra, ${tf:...}, remote state, hooks |
| 12-async-and-context | async handlers, logging, deadlines |
| 13-migrate-from-aws-lambda | Before/after migration of Lambda + Step Functions |
| 14-testing | Unit-testing handlers and workflows with pytest |
| 15-ci-cd-deploy | GitHub Actions deploys per stage |
Documentation
| Getting started | Install, credentials, first deploy |
| Configuration reference | Every pskit.yml key, variables, extends, stages |
| Events | HTTP, schedule, queue, storage, topic, with a per-provider matrix |
| Handler API | Event, Context, Response, errors, BatchResult, invoke() |
| Workflows | State machine reference, engines, limitations |
| Providers | Options, credentials, resources and limits per cloud |
| CLI | Every command and flag |
| Local development | pskit dev, invoke, testing |
| Dashboard | The deployment UI |
| Architecture | Internals and third-party providers |
| Migrating from AWS | Lambda + Step Functions → pskit |
| Troubleshooting | Common errors and fixes |
Contributing & releasing
- Contributor guide: deploy_docs/CONTRIBUTING.md
- Quick PyPI release guide: deploy_docs/How_to_deploy.md
- Publishing to PyPI (step by step): deploy_docs/PUBLISHING_TO_PYPI.md
- Security policy: SECURITY.md
Author
Created and maintained by Asif Khan @ Kavion.ai.
License
MIT, see LICENSE.
Metadata
Release files for py-serverless-kit 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| py_serverless_kit-0.1.0.tar.gz | 232.3 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| py_serverless_kit-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 390.1 kB
Release files / py_serverless_kit-0.1.0.tar.gz
| Download URL | py_serverless_kit-0.1.0.tar.gz |
|---|---|
| Size | 232.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
59bc6d360712107888335d83309d89135b51dd0f5c42230b0a37400ba60741cb
|
|
BLAKE2b-256 checksum How to use checksums |
cc1bda23b2c3d007a19acde6059c4ef997f0582a42cdb9fab85f6d2977c983da
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.10
|
Release files / py_serverless_kit-0.1.0-py3-none-any.whl
| Download URL | py_serverless_kit-0.1.0-py3-none-any.whl |
|---|---|
| Size | 157.8 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
14e68627f23752aa8e91f51951910f6542edf7cf6bbbe5e993f2a5056a46d685
|
|
BLAKE2b-256 checksum How to use checksums |
cebf2bc059583a8546030670e4b269f382df726c686592f5b41d82e98868a4b5
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.10
|