---
title: "Configure Pixeltable Cloud: pixeltable.toml, Secrets, and Deploy Order"
date: "2026-10-02"
author: "Pierre Brunelle"
tags:
  - Pixeltable Cloud
  - Configuration
  - pixeltable.toml
  - Deployment
  - Secrets
  - API Keys
  - MLOps
description: "How a Pixeltable Cloud database is configured: the pixeltable.toml entry, when the image rebuilds, the deploy order, home-bucket media, secrets, and scoped API keys."
url: "https://pixeltable.com/blog/configure-pixeltable-cloud"
---

# Configure Pixeltable Cloud: pixeltable.toml, Secrets, and Deploy Order

**Summary:** A Pixeltable Cloud database is one entry in `pixeltable.toml`. The entry names the database, chooses which project files it receives, and optionally sets the Python environment and the size it runs at. Deploy with `pxt db update`, then `pxt schema update`, then `pxt service update`. Provider keys are secrets. The key your app sends is a separate Pixeltable API key. This is the configuration reference for that path. The reference docs are [Deploy to Pixeltable Cloud](https://docs.pixeltable.com/cloud).

 
Pixeltable Cloud is in Limited Beta. Email [contact@pixeltable.com](mailto:contact@pixeltable.com) for an account, or start with `pxt new`, which creates a trial organization and database `main`. Claim it within 48 hours. The same `app.py` you run locally is the file you deploy. If you are declaring that file for the first time, start with [How to serve a typed AI endpoint](/blog/serve-typed-ai-endpoint-pixeltable).

 
## Two runtimes, one project

 
A hosted database does two jobs, and they are not the same process.

 
| Runtime | What it does | How you start it |
| --- | --- | --- |
| Database | Holds the catalog. Runs pxt.get_table, view iterators, and index builds. You run pxt schema update against it. | pxt db update |
| Service | Serves the HTTP routes FastAPIRouter declares in app.py. | pxt service update |

 
Interactive HTTP does not run inside the catalog process. A code upload updates the project both of them read. An image rebuild updates the Python environment both of them run. Size the database on its `[[pixeltable.database]]` entry. `pxt service update` starts the service on the platform default.

 
## The pixeltable.toml entry

 
`pxt init` marks the project root. Keep the local database entry it writes, and add a hosted one. Replace `myorg` with the slug from `pxt org list`. `pxt org create` already provisions `main`.

 
```toml
[[pixeltable.database]]
name = 'pxt://myorg:main'
```

 
If the project already has a `pyproject.toml`, `pxt init` writes there instead. Use `[[tool.pixeltable.database]]` for the hosted entry. `pixeltable.toml` wins when both files exist.

 
A URI with no matching entry is an error. `pxt db update pxt://myorg:main` looks up the entry whose `name` is that URI. Leave `cpu`, `memory_mb`, `disk_gb`, and `workers` out until you have a reason to change the default.

 
When the default environment or size is not enough, the same entry is where those settings go:

 
```toml
[[pixeltable.database]]
name = 'pxt://myorg:main'
exclude = ["__pycache__", "*.pyc", ".git", ".env", "*.egg-info", ".venv"]
system_dependencies = ["ffmpeg"]
python_version = "3.12"
cpu = 1.0
memory_mb = 2048
disk_gb = 20
```

 

 - The default packages every file git would not ignore, including `app.py` at the project root. `exclude` drops globs from that set. `include` adds files an exclude or `.gitignore` would have dropped. It does not replace the set, so `include = ["app/**"]` does not mean "only the app directory," and it does not drop a root `app.py`. `include_only` is the whitelist. It cannot be combined with `include` or `exclude`. Leave `include` unset for the `app.py` this post deploys.

 - `system_dependencies` are conda-forge package specs, installed with micromamba when the image is built. `ffmpeg` is the usual one. An apt package name is not a spec.

 - `python_version` is `3.12` or `3.12.8`. `uv_options` is an optional extra argument string for `uv sync`.

 - `cpu`, `memory_mb`, and `disk_gb` are the database pod. Memory has to stay between 1,024 and 4,096 MB per vCPU. A size outside that range is refused.

 - Leave `workers` unset. The catalog database runs as one worker. Give it more CPU and memory when schema updates, iterators, or index builds need it. Do not raise the worker count to get more HTTP throughput. That is the service.

 - `disk_gb` is local disk for the file cache. Rows and media are not stored on it.

 

 
`pxt db diff pxt://myorg:main` prints what `update` would change and applies nothing. Exit 0 means the database already matches the entry. Exit 2 means a change is pending. Field reference: [cloud configuration](https://docs.pixeltable.com/platform/cli#cloud-configuration-reference).

 
## Image rebuild or project upload

 
`pxt db update` decides which of two artifacts changed.

 
| What changed | What update does |
| --- | --- |
| app.py, a UDF, a route, any other selected source file | Uploads the project archive. Does not rebuild the image. |
| A dependency, python_version, or a system package | Rebuilds the image. This is the slow step. |
| cpu, memory_mb, disk_gb, or workers | Resizes. The pods restart once. |

 
The image installs packages from `uv.lock` or `requirements.txt`. With neither, `pxt db update` warns and the image contains Pixeltable only, which is enough for the generated example app. Anything else `app.py` imports belongs in one of those files. The first `pxt db update` builds the image and returns when the database is ready. That takes several minutes. Later code-only updates do not.

 
If a command reports an unsupported proxy protocol version, this Pixeltable and the database image disagree. When your local install is newer, upgrade the lockfile pin and run `pxt db build-image pxt://myorg:main`. `pxt db restart` keeps the image the pods already run. When the database is newer, run `pip install -U 'pixeltable[serve]'`.

 
## Deploy order

 
A new database accepts `pxt db update` only after `pxt db status pxt://myorg:main` shows `AVAILABLE`. From the project directory, in order, and with `-f` when there is no terminal to confirm in:

 
```bash
pxt db status pxt://myorg:main
pxt db update pxt://myorg:main -f
pxt schema update app.py pxt://myorg:main -f
pxt service update app.py pxt://myorg:main -f
pxt service list pxt://myorg:main
```

 

 - `pxt db update` uploads the project and applies the entry. It does not insert rows and it does not start routes.

 - `pxt schema update` creates and migrates the tables. It does not start HTTP. Running it before the project is on the database fails with `404: UDF not found`.

 - `pxt service update` starts the hosted routes. `pxt service run` is local only.

 

 
`pxt service list` prints the URL and its routes. The host looks like `https://{org}-{db}.svc.pxt.run/<service>`. Copy it from the command. Do not hardcode it, and do not send service traffic to the control-plane host. Calling that service from TypeScript is [How to serve a typed AI endpoint](/blog/serve-typed-ai-endpoint-pixeltable).

 
## What to run after a change

 
| You changed | Run |
| --- | --- |
| A UDF body, a route, or any .py the entry selects | pxt db update, then pxt service update so running services pick up the upload |
| A column or a table in app.py | pxt db update, then pxt schema update, then pxt service update |
| A dependency, the Python version, or a system package | pxt db update. The image rebuilds. |
| cpu, memory_mb, disk_gb, or workers | pxt db update. One resize. |
| A secret | pxt secret set, then pxt db restart and pxt service restart |
| Nothing, but the last build failed | pxt db build-image pxt://myorg:main |

 
## Where media goes

 
Every Cloud database has a home bucket at `pxtfs://myorg:main/home`. Hosted tables and services write there with no `destination=` and no destination environment variables. Inserting a local file path on a Cloud table uploads the file.

 
Set a destination only in two cases.

 

 - Local Pixeltable writing into that same bucket: sign in with `pxt login` or `PIXELTABLE_API_KEY`, then set `PIXELTABLE_INPUT_MEDIA_DEST` and `PIXELTABLE_OUTPUT_MEDIA_DEST`. See [Cloud storage](https://docs.pixeltable.com/integrations/cloud-storage).

 - Your own bucket: store `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY` (or the GCS or Azure names) with `pxt secret`, and point a column at `s3://`, `gs://`, or `wasbs://`.

 

 
```python
import pixeltable as pxt

TableModel = pxt.model_base()

class Images(TableModel, name='images'):
 photo: pxt.Image
 thumbnail = pxt.Column(
 value=photo.resize((256, 256)),
 destination='s3://my-bucket/thumbnails',
 )
```

 
`db_input_media_dest` and `db_output_media_dest` on the database entry change the default for inserted or computed media. The home bucket remains the default when you set neither.

 
## Secrets: organization, then database

 
`PIXELTABLE_API_KEY` is how the CLI and your backend authenticate to Pixeltable Cloud. `OPENAI_API_KEY` and the other provider keys are secrets. They are not the same value, and provider keys do not belong in `app.py`.

 
An organization secret applies to every database. A database secret applies to that database and overrides the organization value when the names collide.

 
```bash
pxt secret set pxt://myorg OPENAI_API_KEY=your-provider-key
pxt secret set pxt://myorg:main OPENAI_API_KEY=your-prod-key
pxt db restart pxt://myorg:main
pxt service restart pxt://myorg:main/ingest
```

 
A process reads its secrets once, at startup. `pxt secret list` prints names, never values. You can set the same keys in the Cloud dashboard.

 
## API keys for the service

 
The CLI and `pxt.get_table('pxt://myorg:main/docs')` work with a `pxt login` session. A backend that calls the hosted HTTP service needs a key. Keep it on the server. The browser calls your server.

 
```bash
pxt key create my-app
pxt service list pxt://myorg:main
export PIXELTABLE_API_KEY='your-key'
SERVICE_URL='URL from pxt service list'
curl -X POST "$SERVICE_URL/docs" \
 -H "X-api-key: $PIXELTABLE_API_KEY" \
 -H 'Content-Type: application/json' \
 -d '{"title": "Hello from HTTP", "body": "world"}'
```

 
`SERVICE_URL` is the endpoint `pxt service list` printed. The example app's insert route is `/docs` on that URL. A key created without `--grant` acts as you. Cloud checks `X-api-key` first. A request that also sends `Authorization: Bearer` is authenticated as that API key.

 
A key with `--grant` is a runtime key. It belongs to the organization and reaches only its grants. Grants are a preview: Cloud refuses them until they are enabled for the organization. Until then, omit `--grant`.

 
| Grant | What that key can do |
| --- | --- |
| access:pxt://myorg:main/services/ingest | Call the ingest service |
| access:pxt://myorg:main/services | Call any service in main |
| access:pxt://myorg:main | That database's services and its storage |
| manage:pxt://myorg:main/services | Create, start, stop, and delete services. It cannot read what those services return. |

 
`access` and `manage` do not imply each other. The secret prints once. `pxt key update` changes grants without reissuing it. The full table is under [`pxt key`](https://docs.pixeltable.com/platform/cli#pxt-key). For a typed TypeScript client, use the walkthrough in [serve a typed AI endpoint](/blog/serve-typed-ai-endpoint-pixeltable).

 
## Operate it

 
```bash
pxt db status pxt://myorg:main
pxt db logs pxt://myorg:main
pxt service logs pxt://myorg:main/ingest
pxt db restart pxt://myorg:main
pxt db stop pxt://myorg:main
pxt db start pxt://myorg:main
```

 
`stop` releases compute and keeps the data. `pxt db delete` deletes a database and its storage. It refuses the organization's current default database. Set a different default first. A new organization's default is `main`.

 
## Local and Cloud, same file

 
| | Local | Cloud |
| --- | --- | --- |
| Project | pxt init | the same project, plus name = 'pxt://myorg:main' |
| Tables | pxt schema update app.py my_app | pxt schema update app.py pxt://myorg:main |
| HTTP | pxt service update app.py my_app | pxt service update app.py pxt://myorg:main |
| Media | local disk | pxtfs://myorg:main/home |

 
Install with `pip install -U 'pixeltable[serve]'`. Sign in with `pxt login` for an account you already have. The commands above are the configuration. The application is still one `app.py`. Declaring the routes and calling them from Next.js is [How to serve a typed AI endpoint](/blog/serve-typed-ai-endpoint-pixeltable).