{"id":16061101,"url":"https://github.com/amancevice/terraform-aws-serverless-pypi","last_synced_at":"2026-06-03T03:01:06.258Z","repository":{"id":44857266,"uuid":"210429257","full_name":"amancevice/terraform-aws-serverless-pypi","owner":"amancevice","description":"Serverless PyPI backed by S3","archived":false,"fork":false,"pushed_at":"2026-04-11T00:57:50.000Z","size":367,"stargazers_count":59,"open_issues_count":2,"forks_count":9,"subscribers_count":3,"default_branch":"main","last_synced_at":"2026-04-11T02:25:23.663Z","etag":null,"topics":["pypi","python","s3","serverless","terraform"],"latest_commit_sha":null,"homepage":"","language":"HCL","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/amancevice.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2019-09-23T18:52:48.000Z","updated_at":"2026-04-11T00:57:58.000Z","dependencies_parsed_at":"2025-12-31T09:03:47.890Z","dependency_job_id":null,"html_url":"https://github.com/amancevice/terraform-aws-serverless-pypi","commit_stats":null,"previous_names":[],"tags_count":33,"template":false,"template_full_name":null,"purl":"pkg:github/amancevice/terraform-aws-serverless-pypi","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/amancevice%2Fterraform-aws-serverless-pypi","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/amancevice%2Fterraform-aws-serverless-pypi/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/amancevice%2Fterraform-aws-serverless-pypi/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/amancevice%2Fterraform-aws-serverless-pypi/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/amancevice","download_url":"https://codeload.github.com/amancevice/terraform-aws-serverless-pypi/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/amancevice%2Fterraform-aws-serverless-pypi/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33845770,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-03T02:00:06.370Z","response_time":59,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["pypi","python","s3","serverless","terraform"],"created_at":"2024-10-09T04:07:51.681Z","updated_at":"2026-06-03T03:01:06.218Z","avatar_url":"https://github.com/amancevice.png","language":"HCL","funding_links":["https://ko-fi.com/smallweirdnumber"],"categories":[],"sub_categories":[],"readme":"# Serverless PyPI\n\n[![terraform](https://img.shields.io/github/v/tag/amancevice/terraform-aws-serverless-pypi?color=62f\u0026label=version\u0026logo=terraform\u0026style=flat-square)](https://registry.terraform.io/modules/amancevice/serverless-pypi/aws)\n[![test](https://img.shields.io/github/actions/workflow/status/amancevice/terraform-aws-serverless-pypi/test.yml?logo=github\u0026style=flat-square)](https://github.com/amancevice/terraform-aws-serverless-pypi/actions/workflows/test.yml)\n[![coverage](https://img.shields.io/codeclimate/coverage/amancevice/terraform-aws-serverless-pypi?logo=code-climate\u0026style=flat-square)](https://codeclimate.com/github/amancevice/terraform-aws-serverless-pypi/test_coverage)\n[![maintainability](https://img.shields.io/codeclimate/maintainability/amancevice/terraform-aws-serverless-pypi?logo=code-climate\u0026style=flat-square)](https://codeclimate.com/github/amancevice/terraform-aws-serverless-pypi/maintainability)\n\n[![ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/smallweirdnumber)\n\nS3-backed serverless PyPI.\n\nRequests to your PyPI server will be proxied through a Lambda function that pulls content from an S3 bucket and responds with the same HTML content that you might find in a conventional PyPI server.\n\nRequests to the base path (eg, `/simple/`) will respond with the contents of an `index.html` file at the root of your S3 bucket.\n\nRequests to the package index (eg, `/simple/fizz/`) will dynamically generate an HTML file based on the contents of keys under that namespace (eg, `s3://your-bucket/fizz/`). URLs for package downloads are presigned S3 URLs with a default lifespan of 15 minutes.\n\nPackage uploads/removals on S3 will trigger a Lambda function that reindexes the bucket and generates a new `index.html` at the root. This is done to save time when querying the base path when your bucket contains a multitude of packages.\n\n![Serverless PyPI](./docs/serverless-pypi.png)\n\n## Usage\n\nAs of v7 users are expected to bring-your-own REST API (v1). This gives users greater flexibility in choosing how their API is set up.\n\nThe most simplistic setup is as follows:\n\n```terraform\n#######################\n#   SERVERLESS PYPI   #\n#######################\n\nmodule \"serverless_pypi\" {\n  source  = \"amancevice/serverless-pypi/aws\"\n  version = \"~\u003e 7\"\n\n  api_execution_arn             = aws_api_gateway_rest_api.pypi.execution_arn\n  api_id                        = aws_api_gateway_rest_api.pypi.id\n  api_root_resource_id          = aws_api_gateway_rest_api.pypi.root_resource_id\n  event_rule_name               = \"serverless-pypi-reindex\"\n  iam_role_name                 = \"serverless-pypi\"\n  lambda_api_fallback_index_url = \"https://pypi.org/simple/\"\n  lambda_api_function_name      = \"serverless-pypi-api\"\n  lambda_reindex_function_name  = \"serverless-pypi-reindex\"\n  s3_bucket_name                = \"serverless-pypi-us-west-2\"\n\n  # etc …\n}\n\n################\n#   REST API   #\n################\n\nresource \"aws_api_gateway_rest_api\" \"pypi\" {\n  description = \"Serverless PyPI example\"\n  name        = \"serverless-pypi\"\n\n  endpoint_configuration { types = [\"REGIONAL\"] }\n}\n\nresource \"aws_api_gateway_deployment\" \"pypi\" {\n  rest_api_id = aws_api_gateway_rest_api.pypi.id\n\n  triggers = { redeployment = module.serverless_pypi.api_deployment_trigger }\n\n  lifecycle { create_before_destroy = true }\n}\n\nresource \"aws_api_gateway_stage\" \"simple\" {\n  deployment_id = aws_api_gateway_deployment.pypi.id\n  rest_api_id   = aws_api_gateway_rest_api.pypi.id\n  stage_name    = \"simple\"\n}\n```\n\n## S3 Bucket Organization\n\nThis tool is highly opinionated about how your S3 bucket is organized. Your root key space should only contain the auto-generated `index.html` and \"directories\" of your PyPI packages.\n\nPackages should exist one level deep in the bucket where the prefix is the name of the project.\n\nExample:\n\n```plain\ns3://your-bucket/\n├── index.html\n├── my-cool-package/\n│   ├── my-cool-package-0.1.2.tar.gz\n│   ├── my-cool-package-1.2.3.tar.gz\n│   └── my-cool-package-2.3.4.tar.gz\n└── my-other-package/\n    ├── my-other-package-0.1.2.tar.gz\n    ├── my-other-package-1.2.3.tar.gz\n    └── my-other-package-2.3.4.tar.gz\n```\n\n## Fallback PyPI Index\n\nYou can configure your PyPI index to fall back to a different PyPI in the event that a package is not found in your bucket.\n\nWithout configuring a fallback index URL the following `pip install` command will surely fail (assuming you don't have `boto3` and all its dependencies in your S3 bucket):\n\n```bash\npip install boto3 --index-url https://my.private.pypi/simple/\n```\n\nInstead, if you configure a fallback index URL in the terraform module, then requests for a pip that isn't found in the bucket will be re-routed to the fallback.\n\n```terraform\nmodule \"serverless_pypi\" {\n  source  = \"amancevice/serverless-pypi/aws\"\n  version = \"~\u003e 7\"\n\n  lambda_api_fallback_index_url = \"https://pypi.org/simple/\"\n\n  # etc …\n}\n```\n\n## Auth\n\nPlease note that this tool provides **NO** authentication layer for your PyPI index out of the box. This is difficult to implement because `pip` is currently not very forgiving with any kind of auth pattern outside Basic Auth.\n\nUsing a REST API configured for a private VPC is the easiest solution to this problem, but you could also write a custom authorizer for your API as well.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Famancevice%2Fterraform-aws-serverless-pypi","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Famancevice%2Fterraform-aws-serverless-pypi","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Famancevice%2Fterraform-aws-serverless-pypi/lists"}