TL;DR
To deploy an app you built in Cursor, get its code into a git repository, add a Dockerfile and a floo.app.toml, and run floo deploy. floo hosts the repository, gives the app a URL, Postgres, invite-only sign-in for anyone you invite (no Cursor account needed), a scheduled job, logs, rollback, and a custom domain. One prompt to Cursor's agent does the setup.
- One config file. Sign-in, the database, the job, and the domain are lines in
floo.app.toml, reviewed like code. - Sign-in happens at floo's gateway. Your code reads
X-Floo-User-Emailand writes no login page. - Every deploy is a commit. Rolling back took 16 seconds in our run, and the earlier version was serving again.
- Every output below is from a real run on November 6, 2026.
Cursor launched Cursor Apps on November 5, 2026. A Cursor cloud agent builds the app, Cursor provisions Postgres, and a private or team app sits behind Cursor sign-in. Cursor's docs also describe public links, which Teams and Enterprise admins have to turn on. If everyone who needs the app is on your Cursor team, that may be all you need.
This post is for the other cases: the app should stay private but open for people who are not on your Cursor team, such as teammates outside engineering or a client; you need a job that runs every morning, runtime logs, a rollback, or your own domain; or you built it in Cursor on your laptop, and Cursor's docs say Apps needs a cloud agent. Cursor's Apps docs do not describe scheduled jobs, runtime logs, rollback, or custom domains. floo does each of those from one file in the repo.
We tested every step on a throwaway app before publishing. We ran it on floo's own test stack, so we replaced its hostnames and our email in the outputs below with the ones getfloo.com prints and a placeholder. Everything else is as it came back.
What are we deploying?
A launch checklist, the kind of app Cursor builds from one sentence: a page that lists items, a form to add one, and an API behind it. It is Vite for the page and Express for the API, with Postgres for the items. Every morning a job counts what is still open.
browser -> floo gateway (invite-only sign-in) -> Express + Vite build -> Postgres
cron, 09:00 UTC -> scripts/digest.js -> Postgres
The steps are the same for a Next.js app or anything else with a Dockerfile.
Step 1: Get the code into a git repository
floo deploys commits, so the app needs to be in git. Where it lives depends on how you built it:
- A Cursor App or cloud agent on a new repository. Cursor creates it on Cursor Origin, its git host. Origin works with standard git: copy the clone URL from the repo's Code menu and run
git clone https://origin.cursor.com/<owner>/<repo>.git. See Cursor's clone, push and pull page. - A cloud agent on your GitHub repository. Clone it, or keep it on GitHub and connect it to floo as shown after step 3.
- A local Cursor project. If
git statussays it is not a repository, rungit init. The rest of the steps commit for you.
Then install the floo CLI and sign in:
curl -fsSL https://getfloo.com/install.sh | bash
floo auth login
Step 2: Add a Dockerfile and floo.app.toml
floo runs any app that has a Dockerfile and listens on 0.0.0.0:$PORT. floo init does not write the Dockerfile. Ours builds the Vite page in one stage and runs Express in the next:
FROM node:22-slim AS build
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci
COPY . .
RUN npm run build
FROM node:22-slim AS runtime
WORKDIR /app
ENV NODE_ENV=production
COPY package.json package-lock.json ./
RUN npm ci --omit=dev
COPY --from=build /app/dist ./dist
COPY server.js ./
COPY scripts ./scripts
CMD ["node", "server.js"]
Add a .dockerignore with node_modules, .git, .env, and dist. Without it, a local node_modules can land on top of the clean install. For Next.js, copy the standalone Dockerfile from the Next.js guide; other stacks are in Dockerfiles.
Two lines in server.js matter. The server listens on the port floo gives it, and it reads the signed-in person from a header the gateway adds:
const email = req.get("x-floo-user-email");
app.listen(Number(process.env.PORT ?? 3000), "0.0.0.0");
If Cursor wired the app to read the user or the database some other way, this is the place to switch it to X-Floo-User-Email and DATABASE_URL.
Now run floo init launch-checklist. It writes a floo.app.toml with one web service on port 8080 and an AGENTS.md that points your agent at floo's docs. We changed the port to the one Express uses and added the rest:
[app]
name = "launch-checklist"
access_mode = "accounts"
[services.web]
type = "web"
path = "."
port = 3000
ingress = "public"
migrate_command = "node scripts/migrate.js"
[managed.default]
type = "postgres"
[[routes]]
path = "/"
access = "accounts"
service = "web"
[[routes]]
path = "/health"
access = "public"
service = "web"
[cron.daily-digest]
schedule = "0 9 * * *"
command = "node scripts/digest.js"
service = "web"
timeout = 60
What each part does:
access_mode = "accounts"puts floo's hosted, invite-only sign-in in front of every request. See App sign-in.[managed.default]creates Postgres for each environment and injectsDATABASE_URL.migrate_commandcreates the table before traffic moves to a new version. See Postgres.- The two
[[routes]]keep the app behind sign-in but leave/healthpublic, so you and an uptime check can see which version is live without signing in. [cron.daily-digest]runs a script at 09:00 UTC in the web service's image. See Cron jobs.
Step 3: Deploy with floo deploy
Commit and deploy:
git add -A && git commit -m "Launch checklist"
floo deploy
The first run creates a floo-hosted project named after [app] name, adds a git remote called floo, and pushes your commit. You need no GitHub account. If floo deploy stops waiting before the first deploy finishes, the deploy keeps going. Either way, confirm that your commit, not an earlier one, is the version that is live:
floo deploys list --app launch-checklist --json \
| jq -r --arg sha "$(git rev-parse HEAD)" '.data.deploys[] | select(.commit_sha == $sha) | .status'
Ours read live; the build and deploy of our commit took 27 seconds. Later runs of floo deploy push to the same project and wait for that commit. Our second one went from push to ✓ Deploy live in 27 seconds.
The deploy log shows the migration ran before traffic moved:
[web] Running migration: node scripts/migrate.js
Migration completed successfully.
migrations applied
Or deploy from GitHub instead
If the code is on GitHub and you want every push to deploy, skip floo deploy and connect the repository. A person installs the floo GitHub App on the account that owns the repository once; then:
git push origin main
floo apps github connect owner/launch-checklist
floo deploys watch --app launch-checklist --commit $(git rev-parse HEAD)
Deploy a Next.js app with sign-in from Cursor walks through the GitHub App step. We tested the floo deploy path for this post.
Step 4: Check sign-in, then invite people who are not on Cursor
Open the URL in a private window and you get floo's sign-in page. From a terminal:
curl https://launch-checklist-dev.getfloo.app/
# {"error": "authentication_required", "login_url": "https://launch-checklist-dev.getfloo.app/__floo/login?..."}
curl https://launch-checklist-dev.getfloo.app/health
# {"ok":true,"version":"v1"}
You are already a member with the admin role, because you created the app. Invite anyone else by email, whether or not they have Cursor:
floo apps invite teammate@example.com --role member --app launch-checklist
✓ Invited teammate@example.com to 'launch-checklist'.
floo apps invites --app launch-checklist --json showed "status": "pending" and "delivery_status": "sent", with a link that expires in seven days. They sign in with a floo account for that email. App users do not use builder seats and get no access to floo itself. floo apps members --app launch-checklist lists who has accepted, and floo apps member-remove takes someone out.
Your agent can check the app as you, with no browser and no extra key. It sends the floo API key it signed in with, and the gateway passes your identity to the app (call your app):
curl -X POST https://launch-checklist-dev.getfloo.app/api/items \
-H "Authorization: Bearer $(floo auth token)" \
-H "Content-Type: application/json" -d '{"title":"Write the launch email"}'
# {"id":1,"title":"Write the launch email","done":false,"created_by":"you@example.com"} 201
The row went into Postgres with the creator taken from X-Floo-User-Email.
Step 5: Run the daily job once
The [cron.daily-digest] block shipped with the deploy:
floo cron list --app launch-checklist
│ daily-digest ┆ 0 9 * * * ┆ web ┆ yes ┆ - ┆ never │
Run it now instead of waiting for 09:00 UTC:
floo cron run daily-digest --app launch-checklist
✓ Cron job queued; execution has not been confirmed.
Check the result with `floo cron show daily-digest --env dev`.
Queued is not succeeded. About ten seconds later floo cron list showed success. The job runs in the web service's image with the same DATABASE_URL, so scripts/digest.js needed nothing but the pg package. Jobs do not retry; a missed run waits for the next one.
Step 6: Read the logs
The job's output:
floo logs query --app launch-checklist --cron daily-digest --env dev
[cron:daily-digest] ... daily digest: 1 open items
[cron:daily-digest] ... [floo] cron run succeeded
The app's own output, filtered by text, and the requests floo's gateway handled:
floo logs query --app launch-checklist --since 1h --search "listening"
floo logs query --app launch-checklist --requests --since 1h
GET 401 https://launch-checklist-dev.getfloo.app/ (21ms)
GET 200 https://launch-checklist-dev.getfloo.app/health (25ms)
GET 401 https://launch-checklist-dev.getfloo.app/api/items (0ms)
The 401s are the gateway turning away requests with no sign-in before they reach the app. Logs are kept for 7 days and can take about a minute to appear. floo logs tail streams them. See Logs.
Step 7: Roll back a bad deploy
We changed the version string to v2, committed, and ran floo deploy. /health returned v2. Then we went back:
floo deploys list --app launch-checklist
floo deploys rollback launch-checklist <deploy-id-of-v1> --yes
MIGRATE skipped: rollback re-points a previously migrated image and is not a schema downgrade; migrations do not run
[web] Traffic flipped to revision 'floo-launch-checklist-dev-web-a5899ca81887'.
✓ Rolled back launch-checklist to deploy b90d4893-0788-4dc1-951f-f34a1b0faaca.
It took 16 seconds, and /health returned v1 again. floo deploys list --app launch-checklist --json recorded it as a new deploy with "triggered_by": "rollback" and the v1 commit.
Two things to know. A rollback reverts code, not config or the database schema. And your local branch still has the bad commit, so the next floo deploy would ship it again. Fix forward with git revert or a new commit first. See Releases and rollbacks.
Step 8: Publish to prod and add a custom domain
floo deploy ships to dev, which has its own database. When dev is right, publish it to prod, which gets its own database and its own copy of the job:
floo releases promote --app launch-checklist
✓ Promoted launch-checklist → prod (v1)
Prod deploy in progress.
Our first prod deploy was live 90 seconds later. A custom domain is another block in floo.app.toml, and it takes effect on the next prod release:
[domains."checklist.example.com"]
service = "web"
Commit, floo deploy, floo releases promote, then ask floo which DNS records to publish:
floo domains show checklist.example.com --app launch-checklist
CNAME checklist.example.com -> edge.getfloo.com (traffic)
A checklist.example.com -> 34.117.26.37 (traffic_apex)
Publish the CNAME at your DNS provider (or the A record, if the host is a zone apex), plus the _acme-challenge CNAME floo prints for your host so the certificate can issue. We did not point real DNS for this test, so that record is not in the output above. floo domains watch checklist.example.com --app launch-checklist waits until the domain is active. Custom domains has the full record table.
The prompt to paste into Cursor's agent
Open the repository in Cursor and paste this into the agent. Replace launch-checklist with your app name. It follows the steps above and stops where you need to approve something.
Deploy this app to floo. App name: launch-checklist.
1. If `floo --version` fails, install the CLI: curl -fsSL https://getfloo.com/install.sh | bash. Then run `floo auth login` and have me approve the sign-in in my browser.
2. If there is no Dockerfile, add one following https://getfloo.com/docs/guides/dockerfiles.md. The app must listen on 0.0.0.0 and the PORT environment variable. Add a .dockerignore with node_modules, .git, .env, and dist.
3. If there is no floo.app.toml, run `floo init launch-checklist`. Set port on [services.web] to the port the app uses. Set access_mode = "accounts" under [app]. If the app stores data, add [managed.default] with type = "postgres", read DATABASE_URL from the environment, and set migrate_command under [services.web] to a script that creates the tables and is copied into the image.
4. Read the signed-in user from the x-floo-user-email request header. Do not add a login page, session store, or auth library. Remove any sign-in or database wiring that is specific to where this app was built.
5. If the app has a daily task, add a [cron.<name>] block with schedule, command, service = "web", and timeout. Schedules are UTC.
6. Commit everything with git.
7. Run `floo deploy --json`. If it stops waiting before the first deploy finishes, the deploy keeps going. Confirm with `floo deploys list --app launch-checklist --json` that the deploy whose commit_sha matches `git rev-parse HEAD` is live. If it fails, run the command in error.suggestion and try again.
8. Report the URL, and run `floo cron list --app launch-checklist` and `floo logs query --app launch-checklist --since 10m` so I can see the app is healthy.
Then invite people with floo apps invite. That step is yours, because it decides who sees the app.
What we learned running it
- Check the deploy by commit.
floo deploys list --jsonfiltered bygit rev-parse HEADtells you your commit, not an earlier one, is what is live. - Leave a public health route. With the whole app behind sign-in,
/healthwas the quickest way to see that the rollback had putv1back. - Put scripts in the image. The migration and the job run inside the web service's image, so the Dockerfile copies
scripts/. A script left out fails when it runs, not when you deploy. - Rollback is for now; a commit is for later. Roll back to stop the damage, then revert in git so the next deploy does not bring it back.
floo deploys and hosts apps built with AI, whether they are tools for your team or apps for your customers. If you would rather start from a working app with sign-in and Postgres already in place, pick one from the templates. The backend overview lists each part floo supplies, and internal apps compares the ways to host tools for a team. For a longer walkthrough that builds the app from a prompt, read how to build and deploy an internal tool with AI.
Sources
- Cursor: Apps, Build live apps in Cursor (November 5, 2026), Origin, Origin clone, push and pull
- floo docs: deploy without GitHub, floo deploy, Next.js with sign-in from Cursor, Dockerfiles, Express, app sign-in, call your app, Postgres, cron jobs, logs, releases and rollbacks, releases, custom domains, config reference
Frequently asked questions
How do I deploy an app I built in Cursor?
Get the code into a git repository, add a Dockerfile that listens on 0.0.0.0:$PORT, run floo init <name>, and run floo deploy. floo creates a floo-hosted project, pushes your commit, and serves it at a getfloo.app URL. No GitHub account is needed. If the code is already on GitHub, floo apps github connect owner/repo deploys every push instead.
Do the people I share the app with need a Cursor account?
Not on floo. Set access_mode = "accounts" and run floo apps invite person@example.com --app <name>. They get a one-time link and sign in with a floo account for that email. App users do not use builder seats and get no access to floo itself. Cursor's docs say private and team Cursor Apps open only for people signed in to Cursor.
Can I keep using Cursor to change the app after it is on floo?
Yes. floo deploys commits, not a workspace. Keep editing in Cursor, commit, and run floo deploy again, or push to the connected GitHub repository. Each commit gets its own deploy you can find with floo deploys list --app <name> --json.
How do I roll back a bad deploy?
Find the last good deploy ID with floo deploys list --app <name> and run floo deploys rollback <name> <deploy-id> --yes. It re-points traffic at the earlier image without running migrations. In our run it took 16 seconds. Fix forward with a new commit before you deploy again.
Does my app need to be rewritten to run on floo?
Usually not. floo runs any app with a Dockerfile that listens on $PORT. If the app reads the signed-in user or its database from something specific to where it was built, have your agent switch it to the X-Floo-User-Email request header and the DATABASE_URL environment variable.