Your First 10 Minutes with TerminusDB

Open inAnthropic

TerminusDB is a database built for collaboration. In this quickstart you will clone a pre-populated dataset and immediately use the git-for-data workflow: branch, edit, diff, and merge. No schema to write, no data to invent.

Set it up with an AI agent

TerminusDB is local-first: the database runs on your own machine, and with Versioned Search (12.1 release candidate) the embeddings can run on your own hardware too, with no API key.

If you use an AI coding agent such as Claude Code, Codex or Cursor, you can paste this prompt and let it do the setup for you. It uses the docker-compose.yml in the vectorsearch folder of the TerminusDB repository. That is the 12.1 release candidate, with built-in vector search and local embeddings (no API key). Read the steps before you run them, as the agent will run commands on your machine.

Example: Text
Set up a local TerminusDB for me and check that it works. Use Docker Compose.

1. Make a new empty folder called terminusdb-local and work in it.
2. Download these three files from the terminusdb/terminusdb repository, main branch, folder vectorsearch:
   https://raw.githubusercontent.com/terminusdb/terminusdb/main/vectorsearch/docker-compose.yml
   https://raw.githubusercontent.com/terminusdb/terminusdb/main/vectorsearch/.env.example
   https://raw.githubusercontent.com/terminusdb/terminusdb/main/vectorsearch/README.md
3. Copy .env.example to .env. Set TERMINUSDB_ADMIN_PASS to a new random password.
   Leave OPENAI_KEY empty. Do not print the password in your answer.
4. Run: docker compose up -d
   Then run: docker compose ps
5. Wait until http://localhost:6363/api/info answers with HTTP 200 using basic auth
   admin:<password>. Try for up to 60 seconds.
   Then wait until the embeddings model is ready — docker compose logs embeddings-init
   should end with "Model ready." This can take a few minutes on first start.
6. Create a database:
   POST /api/db/admin/agent_demo
   body: {"label": "Agent demo", "comment": "Made by my AI agent"}
7. Add a tiny schema. The @metadata.embedding block is what makes documents
   searchable — the query fetches the fields, the template renders them into text:
   POST /api/document/admin/agent_demo?graph_type=schema&author=agent&message=Add+schema
   body: {"@type": "Class", "@id": "Person", "name": "xsd:string",
          "friends": {"@type": "Set", "@class": "Person"},
          "@metadata": {"embedding": {
            "query": "query($id: ID){ Person(id: $id) { name } }",
            "template": "{{name}}"}}}
8. Insert two documents:
   POST /api/document/admin/agent_demo?author=agent&message=Add+people
   body: [{"@type": "Person", "@id": "Person/ada", "name": "Ada", "friends": []},
          {"@type": "Person", "@id": "Person/alan", "name": "Alan", "friends": ["Person/ada"]}]
9. Run one query with GraphQL:
   POST /api/graphql/admin/agent_demo
   body: {"query": "{ Person { name friends { name } } }"}
10. Index and run one vector search:
    POST /api/index/admin/agent_demo   (empty body)
    then GET /api/search/admin/agent_demo?q=person+named+Ada&count=2
    Check the index status with GET /api/index/admin/agent_demo:
    searchable_documents should be 2, not 0.
11. Report back to me: which commands you ran, the HTTP status of each call,
    the GraphQL result, the search hits, and how to stop everything (docker compose down).
    If a step failed, show the error and stop. Do not guess.

Docs: https://terminusdb.com/docs/get-started/ and https://terminusdb.com/llms.txt

The 12.1 release candidate image reports "version": "12.0.8" in /api/info. That is expected. Step 10 (the manual index call) is optional. It makes the index ready before the first search. Without it, the first search starts the catch-up and may return no results at first.

Automatic embeddings need three things

Automatic embeddings only work when:

  • the server runs with TERMINUSDB_INDEXER_BACKEND=vectorlink (the vectorsearch docker-compose.yml sets this for you),
  • the class has an @metadata.embedding block with a query and a template, as in step 7,
  • and the documents were committed after both were in place. Documents that already exist are indexed on the first search. This can take a moment, so retry if the first search is empty. To do it in advance, run POST /api/index/{org}/{db} once.

See How automatic indexing works for the details and a troubleshooting list.

The rest of this page does the same kind of thing by hand, with a single docker run command.

Step 1 — Start TerminusDB

Pull and run TerminusDB in one command:

Example: Bash
docker run --pull always -d -p 127.0.0.1:6363:6363 \
  -v terminusdb_storage:/app/terminusdb/storage \
  --name terminusdb terminusdb/terminusdb-server:v12

Wait a few seconds for the server to start, then confirm it is running:

Loading…

You should see a JSON response with "authority": "terminusdb://system/data/User/admin" confirming the server is ready.

Not working?

If the command hangs or returns a connection error: Docker may not be running, port 6363 may already be in use, or a previous container named terminusdb may already exist. See Troubleshooting Connection Failures and Authentication Errors for solutions.

Step 2 — Clone a ready-made dataset

Skip the manual database creation and start with real data immediately by cloning a data product to your local terminusdb running in docker:

Clone Quickstart Database

Get the Star Wars dataset on your local TerminusDB — ready to branch and diff.

You now have a fully populated Star Wars database ready to explore — characters, films, planets, starships, and species. Use it to try the full git-for-data workflow: what would have changed if Anakin Skywalker had turned to the Dark Side?

Step 3 — Create a branch

Create a what-if branch to experiment without affecting the original data:

Loading…

Expected output:

{
  "@type": "api:BranchResponse",
  "api:status": "api:success"
}

You now have two branches: main (the cloned data, unchanged) and what-if (a copy, ready for edits).

Step 4 — Edit on the branch

Pose a "what if" — what if Anakin Skywalker fell to the Dark Side? First, fetch his current document:

Loading…

Now PUT it back with four fields changed to tell the Dark Side story: eye_color → yellow, mass → 120, quote → "I am your father.", side → Dark Side:

Loading…

Expected output:

[
  "Person/Anakin%20Skywalker"
]

Why send the full document?

TerminusDB's PUT replaces the entire document. If you omit optional fields, they become null — and will appear in the diff. By sending the full document with only target fields changed, the diff shows exactly what you intended. TerminusDB detects which fields actually changed, regardless of what you sent.

The edit lives only on what-if. On main, Anakin is still himself — blue eyes, Light Side, mass 84, original quote. The branch is your sandbox.

Step 5 — Diff the branches

This is the moment. See exactly what changed between main and what-if:

Loading…

Expected output:

[
  {
    "@id": "Person/Anakin%20Skywalker",
    "eye_color": {
      "@op": "SwapValue",
      "@before": "blue",
      "@after": "yellow"
    },
    "mass": {
      "@op": "SwapValue",
      "@before": 84,
      "@after": 120
    },
    "quote": {
      "@op": "SwapValue",
      "@before": "This is where the fun begins.",
      "@after": "I am your father."
    },
    "side": {
      "@op": "SwapValue",
      "@before": "Light Side",
      "@after": "Dark Side"
    }
  }
]

What just happened? TerminusDB computed a structural diff between two branches. This is not a line-by-line text diff — it is a semantic patch that knows exactly which fields changed, what the old values were, and what the new values are. Each change is a SwapValue operation that can be applied, reversed, or composed with other patches.

Four fields changed: eye_color, mass, quote, and side. You sent the full document, but TerminusDB detected only the differences — no manual tracking, no event sourcing, no change-data-capture pipeline. The database does it natively.

Step 6 — Merge the branch

Apply the changes from what-if back to main:

Loading…

Expected output:

{
  "@type": "api:ApplyResponse",
  "api:status": "api:success"
}

Done. The main branch now reflects the "what if" scenario — Anakin has turned to the Dark Side.


What you just did

In 6 steps you used the full git-for-data workflow:

StepGit equivalentTerminusDB API
Clone datasetgit clonePOST /api/clone
Create branchgit branchPOST /api/branch
Edit on branchcommit on branchPUT /api/document
Diff branchesgit diff main..what-ifPOST /api/diff/{org}/{db} with before_data_version / after_data_version
Mergegit merge --squashPOST /api/apply with before_commit / after_commit

Step 7 — Clean up

Delete the cloned database when you are done experimenting:

Loading…

Confirm action

This will permanently delete the star-wars data product.

To stop TerminusDB entirely:

Example: Bash
docker stop terminusdb && docker rm terminusdb

Your data persists in the terminusdb_storage Docker volume. To remove it entirely: docker volume rm terminusdb_storage.

Next steps

Other ways to run and use TerminusDB

Troubleshooting

Error: Connection refused

You see:

Example: Text
curl: (7) Failed to connect to localhost port 6363 after 0 ms: Connection refused

Cause: TerminusDB is not running, or another process is using port 6363.

Fix:

Example: Bash
# Check if the container is running
docker ps | grep terminusdb

# If not running, start it
docker start terminusdb

# If it doesn't exist, create it
docker run -d --name terminusdb -p 127.0.0.1:6363:6363 \
  -v terminusdb_storage:/app/terminusdb/storage \
  terminusdb/terminusdb-server

Troubleshooting Connection Failures →

Error: 401 Unauthorized

You see:

Example: JSON
{"@type":"api:ErrorResponse","api:error":{"@type":"api:IncorrectAuthenticationError"},"api:status":"api:failure"}

Cause: Wrong username or password. The default local credentials are admin / root.

Fix:

# Verify credentials work
curl -u admin:root http://localhost:6363/api/info

If you set a custom password via TERMINUSDB_ADMIN_PASS when creating the container, use that value instead of root.

Troubleshooting Authentication Errors →

Frequently Asked Questions

Q: Do I need to define a schema before inserting data?

A: No. Using ?raw_json=true on the document endpoint lets you insert any valid JSON without a schema. TerminusDB stores it as-is. When you are ready for type safety, add a schema later — TerminusDB will validate all future inserts against it. See Schema Reference for details.

Q: Can I use TerminusDB without Docker?

A: Yes. You can build from source on Linux or macOS, or run on Kubernetes. Docker is the recommended path because it requires no dependencies and works on all platforms, but it is not the only option.

Q: How do I reset and start fresh?

A: Stop and remove the container (docker stop terminusdb && docker rm terminusdb), then remove the storage volume (docker volume rm terminusdb_storage). Restart with the original docker run command. This gives you a clean instance with no databases.

Was this helpful?