Deployment
This guide walks you through deploying a Kora.js app to the internet. By the end, your app will have a public URL where anyone can use it, with real-time sync across devices.
Time required: ~10 minutes for your first deploy.
The Fastest Path: kora deploy
If you scaffolded your app with npx create-kora-app and chose a sync template, you already have everything needed. One command handles the entire deployment:
kora deployThat's it. Kora will prompt you for a platform, build your app, and deploy it.
New to deployment?
If you've never deployed an app before, we recommend Fly.io. It has a generous free tier and works well with Kora's real-time sync. The guide below walks you through every step.
Step-by-Step: Deploy to Fly.io
Step 1: Create your app
Skip this if you already have a Kora app. Otherwise:
npx create-kora-app my-appWhen prompted:
- Template: Pick "React + Tailwind (with sync)" (the recommended option)
- Package manager: Pick whichever you prefer (pnpm, npm, yarn, or bun)
Then install dependencies and verify it runs locally:
cd my-app
pnpm install
pnpm devOpen http://localhost:5173 in your browser. You should see a working todo app. Close the dev server when you're done (Ctrl+C).
Step 2: Install the Fly CLI
Fly.io needs a small command-line tool installed on your computer.
macOS:
brew install flyctlLinux/WSL:
curl -L https://fly.io/install.sh | shWindows (PowerShell):
pwsh -Command "iwr https://fly.io/install.ps1 -useb | iex"Verify it's installed:
fly versionYou should see a version number like 0.3.x. If you get "command not found", close and reopen your terminal.
Step 3: Create a Fly.io account and log in
If you don't have an account yet, create one at fly.io. The free tier includes enough resources to run a Kora app.
Then log in from your terminal:
fly auth loginThis opens a browser window. Sign in and return to your terminal.
Step 4: Deploy
Make sure you're in your project directory, then run:
kora deployKora will ask:
? Where do you want to deploy?
> Fly.io (recommended for sync apps)Select Fly.io. Kora then:
- Generates a Dockerfile and deployment configuration
- Bundles your server code into a single file
- Builds your client (React app) for production
- Creates your app on Fly.io
- Deploys everything
After a minute or two, you'll see:
✓ Deployment completed: https://my-app.fly.dev
Sync endpoint: wss://my-app.fly.dev/kora-syncOpen that URL in your browser. Your app is live.
Step 5: Verify sync works
Open the URL in two browser tabs (or on your phone). Add a todo in one tab — it should appear in the other tab within a second. That's real-time sync working.
Subsequent Deploys
After your first deploy, Kora remembers your settings. Just run:
kora deployIt reuses your platform, app name, and region from the previous deploy.
Non-Interactive Deploy (CI/CD)
For automated deployments in a CI/CD pipeline, pass all options as flags:
kora deploy --platform=fly --app=my-app --region=iad --confirmThe --confirm flag tells Kora to fail fast instead of prompting.
Managing Your Deployment
Check status
kora deploy statusShows whether your app is healthy, its URL, and sync endpoint.
View logs
kora deploy logsShows recent output from your deployed app. Useful for debugging issues.
Roll back
kora deploy rollbackReverts to the previous deployment. Use this if a deploy breaks something.
Start fresh
kora deploy --resetDeletes all deployment state and generated files. Your next kora deploy will start from scratch.
Deploy to Railway
Railway is another good option, especially if you prefer a dashboard-based workflow.
Prerequisites
Install the Railway CLI:
npm install -g @railway/cli
railway loginDeploy
kora deploy --platform=railwayThe flow is the same as Fly.io — Kora handles Dockerfile generation, bundling, and deployment.
Deploy to AWS
Kora supports two AWS deployment targets: Lightsail Containers (simpler, cheaper) and ECS Fargate (production-grade, scalable). Both use Docker containers and require the AWS CLI.
Prerequisites (both AWS options)
Install Docker Desktop:
Both AWS options build a Docker container image locally before pushing to AWS. You need Docker running on your machine.
Download from docker.com/get-started and make sure Docker Desktop is running (not just installed) before deploying.
Verify:
bashdocker --versionInstall the AWS CLI:
macOS:
bashbrew install awscliLinux:
bashcurl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip" unzip awscliv2.zip sudo ./aws/installWindows:
Download and run the installer from AWS CLI install page.
Configure credentials:
bashaws configureEnter your AWS Access Key ID, Secret Access Key, and preferred region (e.g.,
us-east-1). You can create an access key in the AWS IAM console.Verify it works:
bashaws sts get-caller-identityYou should see your account ID and ARN.
Option A: AWS Lightsail Containers
Best for: Simple deployments, side projects, and small-to-medium apps. Lightsail has predictable pricing starting at $7/month for a nano container.
Additional prerequisite: lightsailctl plugin
Lightsail requires the lightsailctl plugin to push container images. Install it once:
macOS:
brew install aws/tap/lightsailctlLinux:
curl "https://s3.us-west-2.amazonaws.com/lightsailctl/latest/linux-amd64/lightsailctl" -o "/usr/local/bin/lightsailctl"
chmod +x /usr/local/bin/lightsailctlWindows:
Invoke-WebRequest -Uri "https://s3.us-west-2.amazonaws.com/lightsailctl/latest/windows-amd64/lightsailctl.exe" -OutFile "C:\Program Files\Amazon\lightsailctl\lightsailctl.exe"Verify:
aws lightsail push-container-image --helpDeploy
kora deploy --platform=aws-lightsailOr select "AWS Lightsail Containers" when prompted interactively.
Kora will:
- Create a Lightsail container service (nano size, 1 instance)
- Build your Docker image locally
- Push the image to Lightsail via
lightsailctl - Create a deployment with health check configuration
- Return your live URL
Environment variables
Kora automatically forwards these environment variables from your machine to the Lightsail container:
| Variable | Required | Description |
|---|---|---|
DATABASE_URL | For PostgreSQL | Connection string (e.g., postgres://user:pass@host/db?sslmode=require) |
AUTH_SECRET | For auth | Secret key for signing JWT tokens. Generate with openssl rand -base64 32 |
PUBLIC_URL | Optional | Public URL of your app (for OG meta tags, email links, etc.) |
Set them before deploying:
export DATABASE_URL="postgres://user:pass@ep-xxx.us-east-2.aws.neon.tech/neondb?sslmode=require"
export AUTH_SECRET="$(openssl rand -base64 32)"
kora deploy --platform=aws-lightsail --confirmYou can also update environment variables after deployment via the Lightsail console under your container service's deployment settings.
Example output:
✓ Deployment completed: https://my-app.abc123.us-east-1.cs.amazonlightsail.com
Sync endpoint: wss://my-app.abc123.us-east-1.cs.amazonlightsail.com/kora-syncScaling Lightsail
To change the container size or instance count after your first deploy, use the AWS console or CLI:
aws lightsail update-container-service \
--service-name my-app \
--power small \
--scale 2Available powers: nano, micro, small, medium, large, xlarge.
Option B: AWS ECS Fargate
Best for: Production deployments that need auto-scaling, load balancing, and fine-grained control. ECS Fargate runs your containers without managing servers.
kora deploy --platform=aws-ecsOr select "AWS ECS Fargate" when prompted interactively.
Kora will:
- Create an ECR repository for your Docker image
- Create an ECS cluster and CloudWatch log group
- Build and push your Docker image to ECR
- Register an ECS task definition with health checks
- Update or create the ECS service
First-time setup note: ECS Fargate requires networking configuration (VPC, subnets, security groups) that varies by AWS account. On the first deploy, if no service exists yet, Kora registers the task definition and provides the aws ecs create-service command you need to run with your specific VPC settings:
aws ecs create-service \
--cluster my-app \
--service-name my-app \
--task-definition my-app \
--desired-count 1 \
--launch-type FARGATE \
--network-configuration "awsvpcConfiguration={subnets=[subnet-xxx],securityGroups=[sg-xxx],assignPublicIp=ENABLED}" \
--region us-east-1After the service is created, subsequent deploys with kora deploy work automatically.
Using PostgreSQL with AWS
For production AWS deployments, use a managed PostgreSQL service (Amazon RDS, Neon, Supabase) instead of SQLite. Set the DATABASE_URL environment variable before deploying:
Lightsail — Kora automatically forwards DATABASE_URL from your shell to the container. See Environment variables above. You can also update it later via the Lightsail console.
ECS — add DATABASE_URL to the task definition's environment variables or use AWS Secrets Manager.
See Storage backends below for server code examples.
CI/CD with AWS
Both AWS adapters work in non-interactive mode:
# Lightsail
kora deploy --platform=aws-lightsail --app=my-app --region=us-east-1 --confirm
# ECS Fargate
kora deploy --platform=aws-ecs --app=my-app --region=us-east-1 --confirmMake sure your CI environment has AWS credentials configured (e.g., via AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY environment variables).
Deploy a Tauri Desktop App's Sync Server
Tauri desktop apps store data locally in native SQLite. To sync across multiple devices, you deploy the sync server — the desktop binary is distributed separately.
How it differs from web deployment
| Web App | Tauri Desktop App | |
|---|---|---|
| Client | Deployed with the server (static files) | Distributed as a native binary (.dmg, .msi, .deb) |
| Sync server | Same deployment serves both | Deployed independently |
staticDir | Points to ./dist (built frontend) | Not needed (desktop doesn't serve HTML) |
| Sync URL | Derived from page host | Set via VITE_SYNC_URL at build time |
Step 1: Deploy the sync server
From your Tauri project directory:
pnpm deploy:serverThis is the same kora deploy command used for web apps. It works with all supported platforms (Fly.io, Railway, AWS).
TIP
The sync server for a Tauri app is identical to a web app's server — it's just a Node.js process with WebSocket support. Any deployment method in this guide works.
Step 2: Build the desktop binary with the server URL
Once deployed, build the desktop app pointing to your server:
VITE_SYNC_URL=wss://my-app.fly.dev/kora-sync pnpm buildStep 3: Distribute
Share the built installers from src-tauri/target/release/bundle/ with your users. Every installed copy syncs through your deployed server.
For the full Tauri workflow, see the Tauri Desktop Guide.
More Platforms (Coming Soon)
The following platforms appear in the kora deploy interactive prompt but are not yet implemented:
- Render — Web service deployment with automatic HTTPS
- Docker (self-hosted) — Generate a production Docker image for any hosting environment
- Kora Cloud — Managed hosting optimized for Kora apps
Selecting these will show a "not implemented yet" message. For now, use Fly.io, Railway, or one of the AWS options. If you need to deploy to an unsupported platform, see Manual Server Setup below.
What kora deploy Does Under the Hood
You don't need to know this to use it, but if you're curious:
- Generates a Dockerfile in
.kora/deploy/— a recipe for building your app's container image - Bundles your server — combines
server.tsand its dependencies into a singleserver-bundled.jsfile using esbuild - Builds your client — runs
vite buildto create optimized HTML, CSS, and JavaScript for the browser - Generates platform config — creates
fly.toml(Fly.io),railway.json(Railway), or configures AWS resources (ECS/Lightsail) with the right settings - Provisions the app — creates the app on the platform if it doesn't exist yet
- Deploys — pushes the container image and starts your app
All generated files go into .kora/deploy/. Add this to your .gitignore:
.kora/deploy/Advanced: Manual Server Setup
If you want full control over your deployment (or need to deploy to a platform Kora doesn't support yet), you can set up the server manually.
Install the server package
pnpm add @korajs/serverCreate a server entry file
Create server.ts in your project root:
import { createProductionServer, createSqliteServerStore } from '@korajs/server'
const store = createSqliteServerStore({ filename: './kora-server.db' })
const server = createProductionServer({
store,
port: Number(process.env.PORT) || 3001,
staticDir: './dist',
syncPath: '/kora-sync',
})
server.start().then((url) => {
console.log(`Kora app running at ${url}`)
})createProductionServer serves both your built frontend files and the WebSocket sync endpoint on a single port.
Build and run
pnpm build # Build the client with Vite
node --import tsx server.ts # Start the production serverConnect the client
In your app code, point sync to the server:
const app = createApp({
schema,
sync: {
url: 'wss://your-domain.com/kora-sync',
},
})Storage backends
SQLite (default — good for single-server deployments):
import { createSqliteServerStore } from '@korajs/server'
const store = createSqliteServerStore({ filename: './kora-server.db' })PostgreSQL (recommended for production):
pnpm add postgresimport { createPostgresServerStore } from '@korajs/server'
const store = await createPostgresServerStore({
connectionString: process.env.DATABASE_URL,
})Memory (development/testing only — data lost on restart):
import { MemoryServerStore } from '@korajs/server'
const store = new MemoryServerStore()Docker (manual)
If you need a custom Dockerfile:
FROM node:20-alpine
WORKDIR /app
# Install native module build tools
RUN apk add --no-cache python3 make g++
COPY package.json pnpm-lock.yaml ./
RUN npm install -g pnpm && pnpm install --prod
COPY dist ./dist
COPY server.ts ./
ENV PORT=3000
CMD ["node", "--import", "tsx", "server.ts"]Production Checklist
Before sharing your app publicly:
- [ ] Use HTTPS/WSS — Fly.io, Railway, and AWS Lightsail/ECS provide this automatically. If self-hosting, put a reverse proxy (nginx, Caddy) in front of your server.
- [ ] Add authentication — The default server accepts all connections. See Sync Configuration for setting up token-based auth.
- [ ] Use PostgreSQL for production — SQLite works great for development and small deployments, but PostgreSQL is better for production workloads.
- [ ] Add
.kora/deploy/to.gitignore— Generated deployment files shouldn't be committed.
Troubleshooting
"Fly CLI is required but not installed"
Install the Fly CLI and make sure it's in your PATH. See Step 2 above.
"Could not find a server entry file"
Kora looks for server.ts, server.js, src/server.ts, or src/server.js in your project root. If you used a sync template, this file already exists. If not, create one — see Manual Server Setup.
"Name has already been taken" on Fly.io
App names on Fly.io are globally unique. Pick a different name:
kora deploy --app=my-unique-app-nameApp deploys but shows a blank page
Check that your server.ts uses createProductionServer with staticDir: './dist'. The production server needs to know where your built frontend files are.
"Cannot connect to the Docker daemon"
Docker Desktop must be running (not just installed) before deploying to AWS. Open Docker Desktop and wait for the engine to start, then retry kora deploy. You can verify Docker is ready with:
docker info"lightsailctl plugin was not found"
The Lightsail adapter uses the lightsailctl plugin to push container images. Install it before deploying:
macOS:
brew install aws/tap/lightsailctlLinux:
curl "https://s3.us-west-2.amazonaws.com/lightsailctl/latest/linux-amd64/lightsailctl" -o "/usr/local/bin/lightsailctl"
chmod +x /usr/local/bin/lightsailctlSee Lightsail prerequisites for all platforms.
"AWS CLI is not authenticated"
Run aws configure and enter your credentials, or set the AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY environment variables. Verify with:
aws sts get-caller-identityLightsail deploy stuck on "DEPLOYING"
Lightsail container deployments can take 2-5 minutes. Check status with:
kora deploy statusOr directly:
aws lightsail get-container-services --service-name my-appIf the deployment fails, check the container logs:
kora deploy logs"image does not provide linux/amd64 platform"
This happens on Apple Silicon Macs (M1/M2/M3/M4). AWS requires linux/amd64 images, but Docker on Apple Silicon builds linux/arm64 by default.
The kora deploy command handles this automatically for AWS adapters. If you're building manually, add the platform flag:
docker build --platform linux/amd64 -t my-app .Note: Cross-platform builds are slower than native builds. This is normal on Apple Silicon.
ECS service won't start
ECS Fargate requires proper networking. Make sure your security group allows inbound traffic on port 3001 and your subnets have internet access (either public subnets with assignPublicIp=ENABLED or private subnets with a NAT gateway).
Sync not working after deploy
Make sure your client-side sync.url matches the deployed WebSocket URL. If you deployed to my-app.fly.dev, the sync URL is wss://my-app.fly.dev/kora-sync.