Skip to main content

System Requirements

SoftwareVersionRequiredPurpose
Bun1.1.0+YesJavaScript runtime and package manager
Redis7.0+YesBackground job queues (BullMQ) and caching
Turso CLILatestNoCloud SQLite database management
Stripe CLILatestNoLocal webhook forwarding during development

Install Dependencies

1

Install Bun

curl -fsSL https://bun.sh/install | bash
Verify the installation:
bun --version
# Should print 1.1.0 or later
2

Install Redis

brew install redis
brew services start redis
Verify Redis is running:
redis-cli ping
# Should return: PONG
3

Clone and install

git clone https://github.com/bunship/bunship.git my-saas
cd my-saas
bun install
This installs all workspace packages:
PackagePathDescription
@bunship/apiapps/apiMain Elysia API application
@bunship/databasepackages/databaseDrizzle ORM schema and migrations
@bunship/configpackages/configShared configuration
@bunship/emailspackages/emailsReact Email templates
@bunship/edenpackages/edenType-safe API client
@bunship/utilspackages/utilsShared utilities
4

Create your environment file

cp .env.example .env
The sections below walk through every variable in this file.

Environment Configuration

Application Settings

These control the runtime mode and URL references used in CORS headers, email links, and OAuth redirects.
NODE_ENV=development
API_URL=http://localhost:3000
FRONTEND_URL=http://localhost:5173
VariableDescriptionDefault
NODE_ENVdevelopment, production, or testdevelopment
API_URLPublic URL of the API (used in email links and OAuth callbacks)http://localhost:3000
FRONTEND_URLPublic URL of your frontend (used for CORS and redirect URLs)http://localhost:5173

JWT Authentication

BunShip uses a dual-token JWT strategy: short-lived access tokens and long-lived refresh tokens. Each needs its own signing secret.
JWT_SECRET=<access-token-secret>
JWT_REFRESH_SECRET=<refresh-token-secret>
Generate secure random values:
bun -e "console.log(require('crypto').randomBytes(32).toString('base64'))"
Use different values for JWT_SECRET and JWT_REFRESH_SECRET. Both must be at least 32 characters. Never commit these to version control.

Database Setup

BunShip uses Drizzle ORM with Turso (libSQL/SQLite). For local development you can use a file-based SQLite database with zero external dependencies.

Local Development (File-Based)

The default .env.example is already configured for local file storage:
TURSO_DATABASE_URL=file:../../local.db
No additional setup is needed. The database file is created automatically when you run migrations.

Turso Cloud

For staging or production, connect to a Turso cloud database:
1

Install the Turso CLI

curl -sSfL https://get.tur.so/install.sh | bash
2

Authenticate

bash turso auth login
3

Create a database

bash turso db create bunship
4

Get connection credentials

# Database URL
turso db show bunship --url

# Auth token
turso db tokens create bunship
5

Update your .env

TURSO_DATABASE_URL=libsql://your-database-name.turso.io
TURSO_AUTH_TOKEN=your-turso-auth-token

Run Migrations and Seed

# Apply the schema
bun run db:migrate

# Optional: populate demo data
bun run db:seed
The seed creates:
  • Demo user[email protected] / demo123456
  • Demo organization with a Pro plan on trial
  • Sample projects attached to the organization
| Command | Description | |---------|-------------| | bun run db:generate | Generate a new migration after editing the Drizzle schema | | bun run db:push | Push schema directly to the database (development only) | | bun run db:studio | Open Drizzle Studio, a visual database browser | | bun run db:reset | Drop all tables and re-run migrations (destroys all data) |

Stripe Setup

BunShip includes subscription management with free, Pro, and Enterprise tiers. All Stripe integration works in test mode without processing real payments.
1

Create a Stripe account

Sign up at stripe.com. The dashboard starts in test mode by default.
2

Get your API keys

Navigate to Developers > API keys in the Stripe Dashboard and copy your Secret key (starts with sk_test_).
STRIPE_SECRET_KEY=sk_test_xxx
3

Create products and prices

Go to Products in the Stripe Dashboard and create your pricing tiers. You need price IDs for each plan and billing interval:
STRIPE_PRO_MONTHLY_PRICE_ID=price_xxx
STRIPE_PRO_YEARLY_PRICE_ID=price_xxx
STRIPE_ENTERPRISE_MONTHLY_PRICE_ID=price_xxx
STRIPE_ENTERPRISE_YEARLY_PRICE_ID=price_xxx
4

Set up webhooks

Stripe sends events (subscription created, payment failed, etc.) to your API via webhooks.For local development, use the Stripe CLI:
# Install: https://stripe.com/docs/stripe-cli
stripe login
stripe listen --forward-to localhost:3000/webhooks/stripe
The CLI prints a webhook signing secret (whsec_...). Add it to your .env:
STRIPE_WEBHOOK_SECRET=whsec_xxx
For production, add a webhook endpoint in the Stripe Dashboard (Developers > Webhooks) pointing to https://your-domain.com/webhooks/stripe and subscribe to these events:
  • checkout.session.completed
  • customer.subscription.created
  • customer.subscription.updated
  • customer.subscription.deleted
  • invoice.paid
  • invoice.payment_failed
Stripe billing is optional for initial development. If STRIPE_SECRET_KEY is not set, billing endpoints return a configuration error but the rest of the API works normally.

S3 / R2 Storage Setup

File uploads use any S3-compatible object storage. Choose the tab matching your provider.
S3_ENDPOINT=https://s3.amazonaws.com
S3_BUCKET=your-bucket
S3_ACCESS_KEY_ID=AKIAxxxx
S3_SECRET_ACCESS_KEY=xxxx
S3_REGION=us-east-1
Create an S3 bucket in the AWS Console and an IAM user with s3:PutObject, s3:GetObject, and s3:DeleteObject permissions scoped to that bucket.
File storage is optional. If the S3 variables are not set, file upload endpoints return a configuration error but the rest of the API works normally.

Redis Setup

Redis powers two subsystems: BullMQ background job queues and a general-purpose caching layer.
REDIS_HOST=localhost
REDIS_PORT=6379
REDIS_URL=redis://localhost:6379
For environments that require authentication:
REDIS_URL=redis://:your-password@your-host:6379
Redis is required. BunShip will fail to start if it cannot connect to Redis because the BullMQ worker initialization runs at boot.

Email Setup (Optional)

BunShip sends transactional emails (verification, password reset, team invitations) through Resend.
RESEND_API_KEY=re_xxx
EMAIL_FROM="BunShip <[email protected]>"
1

Create a Resend account

Sign up at resend.com and copy your API key.
2

Verify your sending domain

In the Resend dashboard, go to Domains, add your domain, and configure the DNS records Resend provides (SPF, DKIM, DMARC).
3

Update .env

Set RESEND_API_KEY and change EMAIL_FROM to use your verified domain.
During development, Resend’s test mode sends to any email address without a verified domain. If RESEND_API_KEY is not set, emails are logged to the console instead of being sent.

OAuth Providers (Optional)

Google OAuth

  1. Go to the Google Cloud Console.
  2. Create a project (or select an existing one).
  3. Enable the Google+ API.
  4. Under Credentials, create an OAuth 2.0 Client ID.
  5. Add the authorized redirect URI: http://localhost:3000/api/v1/auth/google/callback
GOOGLE_CLIENT_ID=xxxx.apps.googleusercontent.com
GOOGLE_CLIENT_SECRET=xxxx

GitHub OAuth

  1. Go to GitHub Developer Settings.
  2. Create a new OAuth App.
  3. Set the callback URL to: http://localhost:3000/api/v1/auth/github/callback
GITHUB_CLIENT_ID=xxxx
GITHUB_CLIENT_SECRET=xxxx

Verifying the Installation

After completing the setup, start the development server and run through these checks.
bun dev
1

Health check

curl http://localhost:3000/health
Expected response:
{ "status": "ok", "timestamp": "2025-01-28T..." }
2

API documentation

Open http://localhost:3000/docs in your browser. You should see the Scalar-powered OpenAPI documentation listing all available endpoints.
3

Test authentication

If you ran bun run db:seed:
curl -X POST http://localhost:3000/api/v1/auth/login \
  -H "Content-Type: application/json" \
  -d '{"email":"[email protected]","password":"demo123456"}'
A successful response includes accessToken and refreshToken fields.

Troubleshooting

Clear the workspace and reinstall:
bun run clean
bun install
  • File-based database: Ensure TURSO_DATABASE_URL=file:../../local.db is set in your apps/api/.env file.
  • Turso cloud: Verify the auth token is still valid:
turso db tokens validate YOUR_TOKEN
Check that Redis is running and reachable:
redis-cli ping
# Expected: PONG
If you are using Docker, confirm the container is up:
docker ps --filter name=redis
Find and stop the process occupying the port:
lsof -i :3000
kill -9 <PID>
Or start BunShip on a different port:
PORT=3001 bun dev
Ensure both secrets are set and at least 32 characters long:
echo $JWT_SECRET | wc -c
# Should print 33 or more (32 chars + newline)
Make sure STRIPE_WEBHOOK_SECRET matches the secret shown by the Stripe CLI or the Stripe Dashboard. For local development, always use the Stripe CLI:
stripe listen --forward-to localhost:3000/webhooks/stripe
The CLI prints a new signing secret each time it starts. Copy it into your .env.

Complete .env Reference

Below is the full list of environment variables from .env.example:
VariableRequiredDefaultDescription
NODE_ENVYesdevelopmentRuntime environment
API_URLYeshttp://localhost:3000Public API URL
FRONTEND_URLYeshttp://localhost:5173Public frontend URL
DATABASE_URLYesfile:./local.dbTurso/SQLite connection string
DATABASE_AUTH_TOKENNoTurso cloud auth token
REDIS_HOSTYeslocalhostRedis hostname
REDIS_PORTYes6379Redis port
REDIS_URLYesredis://localhost:6379Redis connection URL
JWT_SECRETYesAccess token signing secret (32+ chars)
JWT_REFRESH_SECRETYesRefresh token signing secret (32+ chars)
STRIPE_SECRET_KEYNoStripe API secret key
STRIPE_WEBHOOK_SECRETNoStripe webhook signing secret
STRIPE_PRO_MONTHLY_PRICE_IDNoStripe price ID for Pro monthly
STRIPE_PRO_YEARLY_PRICE_IDNoStripe price ID for Pro yearly
STRIPE_ENTERPRISE_MONTHLY_PRICE_IDNoStripe price ID for Enterprise monthly
STRIPE_ENTERPRISE_YEARLY_PRICE_IDNoStripe price ID for Enterprise yearly
RESEND_API_KEYNoResend email API key
EMAIL_FROMNoSender address for transactional emails
S3_ENDPOINTNoS3-compatible storage endpoint URL
S3_BUCKETNoStorage bucket name
S3_ACCESS_KEY_IDNoS3 access key
S3_SECRET_ACCESS_KEYNoS3 secret key
S3_REGIONNous-east-1S3 region
GOOGLE_CLIENT_IDNoGoogle OAuth client ID
GOOGLE_CLIENT_SECRETNoGoogle OAuth client secret
GITHUB_CLIENT_IDNoGitHub OAuth client ID
GITHUB_CLIENT_SECRETNoGitHub OAuth client secret
TOTP_ISSUERNoBunShipName shown in authenticator apps
DEMO_MODENofalseRestricts destructive actions for demo environments
CRON_SECRETNoShared secret for cron job endpoints