## The short version

Deploy a public directory built with Flask on Ample using the public media library pattern. Compute runs the app in an isolated microVM behind a public HTTPS URL, a managed PostgreSQL 16 database is auto-provisioned and injected as DATABASE\_URL, a private S3-compatible bucket holds objects with credentials delivered as encrypted environment variables, and a published bucket serves public assets from the CDN host.

## WHAT WAS VERIFIED

Verified on Flask: an object written to a published bucket with an immutable Cache-Control header, a mapping row, and the CDN URL serving it publicly.

## INFRASTRUCTURE

### What it needs

- [Compute](/content/discover/primitive/compute/index.html): verified (Apps run in isolated x86\_64 Firecracker microVMs; sizes are the priced VM sizes.)
- [Postgres](/content/discover/primitive/postgres/index.html): verified (Managed PostgreSQL 16 runs in its own microVM and is auto-provisioned when an app needs a database and no DATABASE\_URL is supplied.)
- [S3-compatible object storage](/content/discover/primitive/s3-compatible-object-storage/index.html): verified (Buckets are S3-compatible with issued credentials; PutObject and GetObject are verified by canary. Other S3 operations are not verified.)
- [CDN](/content/discover/primitive/cdn/index.html): verified (A published bucket's objects are served at its public\_url. It does not front app compute and is not a cache or key-value store.)

## PREREQUISITES

### Before you start

- A Flask project that builds and starts with the documented commands (pip install into .ample/python from requirements.txt, then waitress from app.py reading PORT on the python-3.12 template)
- A PostgreSQL driver reading DATABASE\_URL at runtime (auto-provisioned when omitted, or supplied with --env)
- A bucket from `ample bucket create` with its credentials passed as encrypted S3\_\* environment variables and a second, published bucket for public assets (CDN\_\*)
- An Ample account token with servers:write, databases:read, buckets:read

## STEP BY STEP

### How to do it

1.  Build and start
   
    pip install into .ample/python from requirements.txt, then waitress from app.py reading PORT on the python-3.12 template. The server must bind 0.0.0.0 on PORT.

2.  Implement the pattern on PostgreSQL
   
    The fixture's module implements public media library: an object written to a published bucket with an immutable Cache-Control header, a mapping row, and the CDN URL serving it publicly. Copy the approach into your schema; keep migrations idempotent and run them with --release-command.

3.  Wire object storage
   
    Create the bucket(s), then pass endpoint, region, bucket and keys as --env values. Use path-style addressing. Keep private data in an unpublished bucket. Publish only the bucket that serves public assets and reference its CDN URL.

4.  Deploy
   
    Run the synchronous deploy once and read the result (exit 0 live, 1 failed, 2 blocked). Re-running with no change is a no-op.
   
    ```
    ample deploy . --name <app-name> --public --start "python3 app.py" --env S3_ENDPOINT=... --env S3_REGION=... --env S3_BUCKET=... --env S3_ACCESS_KEY_ID=... --env S3_SECRET_ACCESS_KEY=... --env CDN_PUBLIC_URL=...
    ```

5.  Verify
   
    Fetch the live URL and the pattern self-test route(s) (/p/public-media-library) from the example; then run your own checks. On failure read `ample logs <deployment_id> --kind build` then `--kind runtime`.
   
    ```
    ample logs <deployment_id> --kind build
    ```

## EXAMPLES

### Tested examples

- **Flask pattern fixture** (`tests/deploy-canaries/flask-patterns`): Multi-pattern Flask app whose public media library module was checked live; the module is under tests/deploy-canaries/_pattern-modules.

## SUCCESS CHECKS

### How to know it worked

- app responds on its public URL (`/` on the live URL, expect ample canary flask patterns)
- public-media-library self-test (`/p/public-media-library` on the live URL, expect a CDN URL whose content is served publicly)

## COST

### Cost estimate

Estimated 10.00 USD per month (size prices from pricing.toml at build revision ca0b484a7a9957c573a88d3a32dc603c3c10135a).

- app server x1 `s-1vcpu-1gb`: 5.00 USD
- managed PostgreSQL database x1 `s-1vcpu-1gb`: 5.00 USD

Upper-bound monthly price of the tested sizes. Buckets are allocation-priced per quota and not included.

## EVIDENCE

### Verification evidence

- canary\_run on 2026-09-21T01:14:18Z at revision `50dbac567d2c5cc8e55e6c748a646be0025d9cfb (CLI 0.1.21)`: Flask pattern fixture deployed on Ample (pip install into .ample/python from requirements.txt, then waitress from app.py reading PORT on the python-3.12 template); checks passed for public-media-library and configuration-secrets. Pattern proof: an object written to a published bucket with an immutable Cache-Control header, a mapping row, and the CDN URL serving it publicly. (expires 2027-03-20T01:14:18Z)
- canary\_run on 2026-09-20T01:54:51Z at revision `199ff1dfd52683832ae75d3f98b53a7a4bff7f96 (CLI e3181f5)`: The same Flask app deployed with a --release-command migration; the marker it created was readable after activation. (expires 2027-03-19T01:54:51Z)

## EXECUTION

### Execution binding

MCP tool `ample_deploy` (registry `mcp:ample_deploy`), schema hash `876465fce906da0c` observed 2026-09-22T00:31:22.774679+00:00 at revision `e6446ceea69b`. Binding state at export: schema\_changed. Required scopes: servers:write, databases:read, buckets:read.

## NEXT ACTIONS

### Typed next actions

- Browse the catalog index (`GET /v1/catalog` on the api origin; authentication not required, approval not required)
- Search published recipes by intent, stack and constraints (`POST /v1/catalog/search` on the api origin; authentication not required, approval not required)
- Prepare a side-effect-free deployment plan for an authorized project (`POST /v1/catalog/plan` on the api origin; authentication required, approval not required)
- Read the existing agent authentication setup (`GET /mcp/setup` on the api origin; authentication not required, approval not required)
- Browse Public directory (`GET /v1/catalog/nodes/workload%3Apublic-directory` on the api origin; authentication not required, approval not required)
- Browse Flask (`GET /v1/catalog/nodes/stack%3Aframework-flask` on the api origin; authentication not required, approval not required)
- Browse Public media library (`GET /v1/catalog/nodes/pattern%3Apublic-media-library` on the api origin; authentication not required, approval not required)
- Browse Deploy web app (`GET /v1/catalog/nodes/intent%3Adeploy-web-app` on the api origin; authentication not required, approval not required)

### About this recipe

#### Identifiers and status

Recipe `page:recipes:public-directory:flask:public-media`. Revision r1.

release **published**support **verified**execution **blocked (schema\_changed: live tool schema differs from the recorded binding)**

Formats: [HTML](/content/recipes/public-directory/flask/public-media/index.html) \| [Markdown](/content/recipes/public-directory/flask/public-media.md) \| [JSON contract](https://api.ample.computer/v1/catalog/recipes/page%3Arecipes%3Apublic-directory%3Aflask%3Apublic-media)
