Operations

Quickstart

Run Layer on your laptop with a local Postgres database or your existing Turbopuffer account. You’ll need Docker with Compose, Git, and curl. The local database needs no account, API key, or license key.

1. Clone and start

Choose your backend. Postgres keeps your data on your machine. For Turbopuffer, replace tpuf_... with your API key before you run the tab.

git clone https://github.com/hev/layer.git
cd layer
docker compose up -d --wait
curl --fail http://localhost:8080/health
git clone https://github.com/hev/layer.git
cd layer
export TURBOPUFFER_API_KEY="tpuf_..."
docker compose up -d --wait
curl --fail http://localhost:8080/health

The gateway listens on localhost:8080. Compose includes a Postgres database with pgvector and pg_search, which the gateway uses unless TURBOPUFFER_API_KEY is set in your shell. Setting the key selects Turbopuffer instead. The Compose file runs the latest release; set GATEWAY_IMAGE=hevlayer/layer-gateway:edge only to opt into the development build. Allow a few minutes for the first image downloads.

Compose also starts embed, the bundled CPU embedding service (hevlayer/layer-embed), and hands the gateway its address as LAYER_EMBED_URL. Its image carries the two CPU text models, so it needs no key, GPU or download after the pull. It listens only on an internal Compose network with no route out of your machine and no host port. Override its image with EMBED_IMAGE the same way as GATEWAY_IMAGE.

For CI setup and standalone configuration, see the repository setup guide.

2. Write rows

The first write creates the namespace. Run this in the same shell as step 1.

curl --fail-with-body http://localhost:8080/v2/namespaces/products \
  -H 'Content-Type: application/json' \
  -d '{
    "distance_metric": "cosine_distance",
    "schema": {"title": {"type": "string", "full_text_search": true}},
    "upsert_rows": [
      {"id": "earbuds", "title": "wireless earbuds", "vector": [1, 0, 0]},
      {"id": "speaker", "title": "portable speaker", "vector": [0, 1, 0]}
    ]
  }'
curl --fail-with-body http://localhost:8080/v2/namespaces/products \
  -H "Authorization: Bearer $TURBOPUFFER_API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{
    "distance_metric": "cosine_distance",
    "schema": {"title": {"type": "string", "full_text_search": true}},
    "upsert_rows": [
      {"id": "earbuds", "title": "wireless earbuds", "vector": [1, 0, 0]},
      {"id": "speaker", "title": "portable speaker", "vector": [0, 1, 0]}
    ]
  }'

3. Query

curl --fail-with-body http://localhost:8080/v2/namespaces/products/query \
  -H 'Content-Type: application/json' \
  -d '{"rank_by": ["vector", "ANN", [1, 0, 0]], "top_k": 1, "include_attributes": true}'
curl --fail-with-body http://localhost:8080/v2/namespaces/products/query \
  -H "Authorization: Bearer $TURBOPUFFER_API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"rank_by": ["vector", "ANN", [1, 0, 0]], "top_k": 1, "include_attributes": true}'

The first row has id: "earbuds" and $dist: 0.

Continue with the API docs for client SDKs and query options, or explore the demos to see what you can build.

esc