> ## Documentation Index
> Fetch the complete documentation index at: https://docs.ankra.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Lovable to Ankra

> Move an app you built in Lovable onto your own Kubernetes cluster with open-source components - add the repository in Ankra's add-application wizard, let Ankra's AI analyse it and open the setup pull request, run the front end from one Dockerfile, and run the backend as Postgres and object storage on your own cluster instead of a hosted service.

Lovable builds the app. Ankra runs it on your cluster, with open-source software you control, behind HTTPS, with every change built and scanned first.

You keep prompting in Lovable. Lovable commits to your GitHub repository, and Ankra builds every commit.

![An app built in Lovable is synced to your GitHub repository. Ankra's add-application wizard analyses it and opens a setup pull request. Ankra builds, scans and deploys it to your cluster, where it runs next to open-source Postgres and object storage that Ankra also runs. A live HTTPS address is printed, and every later edit in Lovable is rebuilt and redeployed.](https://mintcdn.com/ankra/-7zb4asxjhThngoc/images/use-cases/lovable-to-ankra.svg?fit=max\&auto=format\&n=-7zb4asxjhThngoc\&q=85\&s=3886e1805c8c000ce5b6b757988401fb)

From a Lovable repository to your own cluster

***

## What Ankra does for you

Adding an application is a wizard, and the work is done by Ankra's AI, not by you.

| You | Ankra's wizard and AI agent |
| - | - |
| Pick the repository and branch | Reads the repository, detects the framework, and works out what it needs: port, secrets, database |
| Review one pull request | Opens a setup pull request with the Kubernetes manifests, the deploy options (`.ankra/ankra.yaml`) and the build pipeline (`.ankra/pipeline.yaml`) |
| Press Deploy | Builds the image in your cluster, scans it with Semgrep, Checkov and Trivy, applies your image gate, deploys, requests the certificate and prints the live address |
| Approve the plan | If the app needs Postgres, generates a CloudNativePG database in your cluster and wires `DATABASE_URL` to it, so you never copy a password |

You can drive the same flow from chat, which is useful when you want the AI to explain or change the plan before anything is written:

```bash theme={null}
ankra chat "add my repository <owner>/<repo> as an application and show me the setup plan" --mode agent
ankra chat "revise the setup: high availability, 3 replicas" --mode agent
```

Agent mode stops at a confirmation card before every write. See [Applications](/concepts/applications.md) and [AI Assistant](/platform/ai-assistant.md).

***

## Chat and deploy from inside Lovable

The Ankra — Chat & Deploy extension for Chrome puts an **Ask Ankra** button on your Lovable project. From that panel you can chat with Ankra AI, connect the project's GitHub repository and deploy it to your cluster, without switching to the portal.

**1. Install and connect**

The Chrome Web Store listing is in review, so for now [install the extension manually](#install-the-extension-without-the-chrome-web-store). Open a project on `lovable.dev` and click **Ask Ankra**. Choose **Connect to Ankra**, sign in, and approve the extension in the browser sign-in screen the CLI also uses. An Ankra API token works too.

**2. Pick where it deploys**

In the panel's settings, choose your organisation, the target cluster and the GitHub credential Ankra should use. Chat needs only the organisation. If your organisation has no usable cluster, **Create demo cluster** requests a temporary [Playground](/platform/billing.md#playground-environments) and selects it once it is ready.

**3. Connect GitHub and deploy**

Choose **Deploy project**, then **Connect GitHub**. The extension opens Lovable's Git settings and reads the repository and branch from there. If the project is not connected yet, you finish account choice and GitHub authorisation in Lovable. Check the repository and branch, then click **Deploy to Ankra**.

The extension reuses an application with the same repository and branch, or registers a new one, so the first deploy runs the same setup pull request, pipeline and scans described below. If Ankra is not ready yet, for example while the setup pull request or the first build is pending, the request waits and is sent once Ankra accepts it, for up to 24 hours, even if you close the panel.

> [!NOTE]
>
> **Deployment requested** means Ankra accepted the request, not that the app is live. Open the application in Ankra to follow the build and get the address. The extension does not move database contents, Lovable Cloud data or Supabase secrets.

The extension runs only on `lovable.dev` and talks only to `platform.ankra.app`. It reads the project's address and title and the repository and branch Lovable shows in its Git settings, never cookies, passwords or page contents. Your sign-in and recent chats are kept in local extension storage, and **Disconnect** clears them. Chat in the panel uses Ask mode; continue writes that need approval in the portal.

### Install the extension without the Chrome Web Store

The Chrome Web Store listing is in review. Until it is live, load the same package into Chrome yourself:

1. Download [ankra-chrome-extension.zip](https://ankra.ai/downloads/ankra-chrome-extension.zip) and unzip it into a folder you will keep. Chrome loads the extension from that folder, so do not delete it.
2. Open `chrome://extensions` and turn on **Developer mode** in the top-right corner.
3. Choose **Load unpacked** and select the unzipped folder.
4. Reload any open Lovable tab, then click **Ask Ankra**.

A manually loaded extension does not update itself. When the store listing is live, remove this copy from `chrome://extensions` and install the extension from the store to get updates. The same steps work in other Chromium browsers such as Edge and Brave, from that browser's own extensions page.

***

## Add the app with the wizard

**1. Connect Lovable to GitHub**

In Lovable, open the project's GitHub settings and connect a repository. Lovable commits every change to it from then on. This is the only link between the two.

**2. Add the repository in Ankra**

In the portal, open **Applications** and choose **Add application** (**What are you shipping?** in the new portal). Pick your GitHub credential, the repository and the branch Lovable tracks. Ankra analyses it in the background.

Without the portal, from a clone of the repository:

```bash theme={null}
ankra application add . --name shop --branch main --wait
```

**3. Let Ankra write the Dockerfile**

A Lovable project is a Vite single-page app: it builds static files and starts no process. Ankra recognises that shape from `package.json` (Vite, a `build` script, no `start` script) and writes the Dockerfile for it. You add nothing to the repository.

The Dockerfile builds the app with Vite and serves the result from an unprivileged nginx on port 8080, running as a non-root user:

* A refresh on `/dashboard/settings` returns the app instead of a 404, while a missing file under `/assets/` is still a 404.
* Hashed assets are cached for a year and `index.html` never is, so a new release is picked up on the next load.
* `/healthz` answers without touching anything, so the cluster has something to probe.

**4. Review the setup pull request and ship**

Read the pull request Ankra opened: the Dockerfile, the manifests, the deploy options and the pipeline. It is a proposal, and builds run from the next push whether or not you merge it. Then deploy from the wizard, or in one command:

```bash theme={null}
ankra application ship . --cluster <cluster-id>
```

Ankra waits for the build, deploys, waits until the pods run and prints:

```
Live: https://<app>.<cluster-domain>
```

> [!NOTE]
>
> The first pipeline definition needs a person to approve it once. If the build stops with a refused step, run `ankra pipeline get --application shop --latest`, which prints the exact approve command, and run it as yourself.

***

## Run the backend on your cluster

A Lovable app is a front end plus a backend, and the backend is usually a hosted service. On Ankra the backend is open-source software on your own cluster, run by Ankra.

**What Ankra runs for you today**

| Need | Open-source component | How you get it |
| - | - | - |
| Relational database | PostgreSQL, run by the CloudNativePG operator | The wizard generates it when the app needs Postgres and wires `DATABASE_URL`. Or add it as a [managed service](/guides/set-up-a-managed-service.md) |
| File storage (S3 API) | SeaweedFS | [Managed service](/guides/set-up-a-managed-service.md) |
| Cache | Valkey | [Managed service](/guides/set-up-a-managed-service.md) |
| Queue | NATS with JetStream | [Managed service](/guides/set-up-a-managed-service.md) |
| Search | OpenSearch | [Managed service](/guides/set-up-a-managed-service.md) |

The database is a Kubernetes resource in your repository, so it is reviewed in the same pull request as the app, sized by the deploy mode (one instance for quick, three for high availability), and protected like any other data with [backups and restore points](/guides/backups.md). Managed services are in closed beta and enabled per organisation; [contact support](/platform/support.md) to turn them on.

### The part to be honest about: the data layer

Lovable generates apps that talk to **Supabase** with `supabase-js`: sign-in, tables, storage and functions all go through Supabase's own API. Ankra does not recognise Supabase, and its wizard does not rewrite application code, so a repository that calls Supabase keeps calling Supabase unless the data layer changes. There are two ways to get fully onto your cluster:

1. **Replace the data layer in the code.** Move tables to the Postgres Ankra generated, files to the S3 service, and sign-in to an open-source identity provider such as [Ory Kratos](/use-cases/production-multi-cluster-platform.md#step-6-infrastructure-cluster-a-staff-sign-in-realm-with-kratos-and-hydra), Keycloak or authentik. This is a code change, and it is the right job for an AI coding assistant working in the repository, with the Ankra skills installed so it knows where the pieces live:

   ```
   Replace Supabase in this app with the open-source stack on my Ankra cluster.
   Use the Postgres the setup PR generated (DATABASE_URL), keep schema changes in
   versioned migrations, and put files in the S3 service. Show me the plan and the
   diff before you commit.
   ```

2. **Run Supabase's open-source stack on your cluster.** Supabase is open source and can be self-hosted, which keeps the app's code unchanged because only its address changes. Ankra can convert a Supabase self-host Docker Compose file with [`ankra migrate`](/guides/migrate-from-docker) and restore its Postgres through a backup vault, but there is no one-step Supabase package yet, and the compose file's mounted configuration needs care. Treat this as an advanced route.

> [!WARNING]
>
> Whichever you choose, never ship the Supabase **service role** key in the repository or in any `VITE_` variable, and review Row Level Security or its replacement before real users arrive. Do not switch the data layer in production first: prove it in development.

***

## Use your own domain

Add two options to the deploy:

```bash theme={null}
ankra application ship . --cluster <cluster-id> \
  --set ingress_enabled=true \
  --set ingress_host=app.example.com
```

Ankra's DNS automation covers the generated subdomain. For a zone of your own, store a DNS credential and serve the zone from the cluster once:

```bash theme={null}
ankra org dns credentials create --name example-dns
ankra cluster custom-dns-zones add <cluster> --zone example.com --credential example-dns
```

If the name points at Lovable's hosting today, remove it there first. See [Domains and DNS](/guides/avura-domains.md).

***

## Development, then production

Add a second cluster so each change is proved before production sees it. An application deploys to as many environments as you give it, and each environment is a cluster and namespace with its own database and secrets.

![An app built in Lovable is committed to GitHub. An Ankra pipeline tests, builds, scans and gates it and publishes an immutable tag. The tag is deployed to a development cluster and proved there, then promoted unchanged to a production cluster. Each environment talks to its own backend, and one image serves both because the backend address is injected when the container starts.](https://mintcdn.com/ankra/-7zb4asxjhThngoc/images/use-cases/lovable-to-ankra-environments.svg?fit=max\&auto=format\&n=-7zb4asxjhThngoc\&q=85\&s=8f050ba2316cfd9a554eeaa5bb814db1)

One image, two environments: the backend address is injected when the container starts

**1. Deploy to development**

```bash theme={null}
ankra application ship . --cluster <dev-cluster-id> --namespace shop-dev \
  --set ingress_enabled=true --set ingress_host=app.example.dev
```

Each environment gets its own database, so development data never reaches production.

**2. Promote the tag that passed**

In the portal, open the application's **Settings** → **Push to deploy**. Under **What follows pushes**, leave development on and turn production off, so production never follows a push. Then promote the exact tag you tested. Promotion moves the image tag only: never a rebuild, never configuration.

```bash theme={null}
ankra application deploy <application-id> --cluster <prod-cluster-id> --namespace shop \
  --set image_tag=sha-<the-tag-that-passed> \
  --set replicas=2 --mode high_availability \
  --set ingress_enabled=true --set ingress_host=app.example.com
```

You can also promote from the application's **Environments** page in the portal, or from chat with `promote_application`. To roll back, deploy an earlier tag the same way.

Anything that differs per environment and is baked into a Vite build, such as a public API address, can be filled in when the container starts instead of at build time:

> **Dockerfile that reads its backend address at start**
>
> ```dockerfile theme={null}
> # Dockerfile
> FROM node:22-alpine AS build
> WORKDIR /app
> COPY package.json package-lock.json* ./
> RUN if [ -f package-lock.json ]; then npm ci; else npm install; fi
> COPY . .
> ARG VITE_SUPABASE_URL=__SUPABASE_URL__
> ARG VITE_SUPABASE_PUBLISHABLE_KEY=__SUPABASE_PUBLISHABLE_KEY__
> ENV VITE_SUPABASE_URL=$VITE_SUPABASE_URL \
>     VITE_SUPABASE_PUBLISHABLE_KEY=$VITE_SUPABASE_PUBLISHABLE_KEY
> RUN npm run build
>
> FROM nginxinc/nginx-unprivileged:1.27-alpine
> COPY --from=build /app/dist /usr/share/nginx/html
> COPY <<'EOF' /etc/nginx/conf.d/default.conf
> server {
>   listen 8080;
>   root /tmp/site;
>   location = /healthz { return 200 "ok\n"; }
>   location /assets/ { expires 1y; add_header Cache-Control "public, immutable"; try_files $uri =404; }
>   location / { add_header Cache-Control "no-cache"; try_files $uri /index.html; }
> }
> EOF
> COPY --chmod=755 <<'EOF' /docker-entrypoint.d/40-runtime-config.sh
> #!/bin/sh
> set -eu
> rm -rf /tmp/site && mkdir -p /tmp/site && cp -R /usr/share/nginx/html/. /tmp/site/
> swap() {
>   value="$(printenv "$1" || true)"
>   [ -n "$value" ] || { echo "runtime-config: $1 is not set" >&2; exit 1; }
>   escaped="$(printf '%s' "$value" | sed -e 's/[\\&|]/\\&/g')"
>   find /tmp/site -type f \( -name '*.js' -o -name '*.html' \) -exec sed -i "s|$2|$escaped|g" {} +
> }
> swap SUPABASE_URL __SUPABASE_URL__
> swap SUPABASE_PUBLISHABLE_KEY __SUPABASE_PUBLISHABLE_KEY__
> EOF
> EXPOSE 8080
> ```
>
> The same image serves any backend whose address you pass as `SUPABASE_URL` and `SUPABASE_PUBLISHABLE_KEY` (the names follow Lovable's; rename them to match your `.env`), including a self-hosted one. Give each environment its values with an environment secret, which is stored per application, sealed into the workloads and rolled on apply:
>
> ```bash theme={null}
> printf '%s' "$BACKEND_URL" | ankra application env-secrets set <application-id> SUPABASE_URL
> printf '%s' "$BACKEND_KEY" | ankra application env-secrets set <application-id> SUPABASE_PUBLISHABLE_KEY
> ankra application env-secrets apply <application-id>
> ```
>
> Variables in the environment take priority over the `.env` Lovable commits, so the committed address never reaches the bundle. A container started without a variable exits at once and names it.

***

## What you get without asking

* **Scans on every change.** Semgrep over the source, Checkov over the manifests and Trivy over the image, judged against your organisation's gate before anything is published. A generated app is mostly code you did not write, so this is where an unsafe pattern or a leaked key shows up.
* **An immutable tag per commit.** Every deployment is a `sha-` tag.
* **Tests when you add them.** The pipeline has a test stage only when `package.json` has a `test` script, and Lovable projects usually have none.
* **An AI review and a watcher.** Ankra reads every pull request, watches every run, and opens a fix when a build breaks.
* **Pull request previews.** A live preview of each branch, with its own throwaway Postgres if you ask for one. See [Pull request preview environments](/guides/pr-preview-environments.md).

***

## Verify

```bash theme={null}
curl -fsS https://app.example.com/healthz
ankra application deployments <application-id>
ankra cluster get pods -n shop --cluster <cluster-id>
ankra pipeline findings <run-id> --application shop
```

Then open the address, reload a nested page, and sign up and sign in once. A green deployment is not a working app; the sign-in is the check.

***

## Troubleshooting

> **Ankra says it wrote no Dockerfile, or the build cannot find the app**
>
> Ankra writes the Dockerfile for a Vite app that builds static files and names no server of its own. If `package.json` has a `start` script or a `main` file, or a `server.js` sits beside the app, Ankra treats the repository as a Node server instead. If `vite.config` moves the build output out of `dist/`, the generated image cannot find it. In either case add your own `Dockerfile` at the repository root and commit it to the branch Lovable tracks: Ankra builds whatever Dockerfile it finds and never overwrites it.

> **The page loads but refreshing a nested route shows a 404**
>
> The `try_files $uri /index.html;` line is missing from the nginx config, or the config was not copied into the image.

> **Sign-in redirects to localhost or the old address**
>
> The identity provider's allowed redirect list does not include the new address. Add it for each environment.

> **The app still talks to the hosted Supabase**
>
> The code still calls Supabase, which Ankra does not rewrite. Change the data layer as described above, or inject a self-hosted backend's address.

> **The build stops at the image gate**
>
> A scanner found something above your threshold. `ankra pipeline findings <run-id> --application shop` lists it, worst first. Fix it, or accept it deliberately and write down why.

> **The pod restarts and its log says a variable is not set**
>
> Only the runtime-config Dockerfile does this, on purpose. Set the environment secret and run `ankra application env-secrets apply <application-id>`.

> **My own domain shows no certificate or does not resolve**
>
> The zone was not declared for that cluster. Run `ankra cluster custom-dns-zones list <cluster>` and add it with its credential.

***

## Next steps

> **[Applications](/concepts/applications.md)**
>
> How Ankra analyses a repository, what the setup pull request contains and how deployments work.

> **[Managed services](/concepts/managed-services.md)**
>
> PostgreSQL, Valkey, NATS, OpenSearch and S3-compatible storage, run on your cluster.

> **[Backups and restore points](/guides/backups.md)**
>
> Protect the database and the files the app depends on.

> **[Prompt to Production](/use-cases/prompt-to-production.md)**
>
> The same development-then-production discipline for apps you build from a written plan.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.