Getting Started
omnibase is a complete backend platform built on PostgreSQL. It gives you a database, authentication, file storage, realtime subscriptions, and auto-generated APIs — plus extras like Redis caching, CDN, monitoring, and workflow automation.
Everything runs on dedicated infrastructure in the EU (Hetzner, Germany). Your data never leaves European jurisdiction.
1. Access your dashboard
Log in at dashboard.omnibase.live with the email you signed up with. Your dashboard shows your project overview, usage, and service links.
2. Get your API keys
Every project has two keys:
| Key | Purpose | Safe to expose? |
|---|---|---|
| anon (public) | Used in frontend apps. Respects Row Level Security (RLS). | Yes |
| service_role (secret) | Bypasses RLS. Used only in server-side code. | Never |
Find them in Settings → API Keys in your dashboard.
3. Install the client library
# JavaScript / TypeScript
npm install @supabase/supabase-js
# Python
pip install supabase
# Dart / Flutter
dart pub add supabase_flutter4. Initialize the client
import { createClient } from '@supabase/supabase-js'
const supabase = createClient(
'https://your-project.omnibase.live',
'your-anon-key'
)💡 omnibase is fully compatible with the Supabase client libraries. Any Supabase tutorial or example code works with omnibase — just swap the URL and key.
Database
Your project includes a full PostgreSQL 15 database with no row limits — you're only limited by your plan's disk allocation.
Creating tables
Use the Studio SQL editor or any PostgreSQL client. Example:
CREATE TABLE products (
id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
name text NOT NULL,
description text,
price decimal(10,2) NOT NULL DEFAULT 0,
in_stock boolean DEFAULT true,
created_at timestamptz DEFAULT now()
);
-- Enable Row Level Security
ALTER TABLE products ENABLE ROW LEVEL SECURITY;Row Level Security (RLS)
RLS lets you control which rows each user can see or modify. Always enable it on tables that hold user data.
-- Allow anyone to read products
CREATE POLICY "Products are viewable by everyone"
ON products FOR SELECT
USING (true);
-- Only authenticated users can insert
CREATE POLICY "Authenticated users can insert products"
ON products FOR INSERT
TO authenticated
WITH CHECK (true);
-- Users can only update their own products
CREATE POLICY "Users can update own products"
ON products FOR UPDATE
USING (auth.uid() = user_id);⚠️ If you enable RLS on a table without adding any policies, no one can access the data (including the anon key). This is a safe default.
Querying from your app
// Fetch all products
const { data, error } = await supabase
.from('products')
.select('*')
// Insert a product
const { data, error } = await supabase
.from('products')
.insert({ name: 'Widget', price: 9.99 })
.select()
// Update
const { data, error } = await supabase
.from('products')
.update({ price: 12.99 })
.eq('id', 1)
// Delete
const { error } = await supabase
.from('products')
.delete()
.eq('id', 1)Joins and relationships
// Fetch orders with their products (foreign key relationship)
const { data } = await supabase
.from('orders')
.select(`
id,
created_at,
order_items (
quantity,
products ( name, price )
)
`)Database functions
// Create a server-side function
CREATE OR REPLACE FUNCTION get_total_revenue()
RETURNS decimal AS $$
SELECT COALESCE(SUM(amount), 0) FROM orders WHERE status = 'paid';
$$ LANGUAGE sql SECURITY DEFINER;
// Call it from your app
const { data } = await supabase.rpc('get_total_revenue')REST API
Every table and view in your database automatically gets a RESTful API via PostgREST. No code generation needed — create a table and it's instantly available.
Base URL
https://your-project.omnibase.live/rest/v1/Authentication
Include your API key in every request:
curl https://your-project.omnibase.live/rest/v1/products \
-H "apikey: YOUR_ANON_KEY" \
-H "Authorization: Bearer YOUR_ANON_KEY"Filtering
| Operator | URL syntax | Meaning |
|---|---|---|
| eq | ?column=eq.value | Equals |
| neq | ?column=neq.value | Not equals |
| gt / lt | ?column=gt.100 | Greater / less than |
| gte / lte | ?column=gte.100 | Greater/less than or equal |
| like | ?column=like.*widget* | Pattern match (case-sensitive) |
| ilike | ?column=ilike.*widget* | Pattern match (case-insensitive) |
| in | ?column=in.(a,b,c) | In list |
| is | ?column=is.null | Is null / is true / is false |
Pagination
# Get rows 0-9
GET /rest/v1/products?limit=10&offset=0
# With ordering
GET /rest/v1/products?order=created_at.desc&limit=10Inserting and updating
# Insert
curl -X POST https://your-project.omnibase.live/rest/v1/products \
-H "apikey: YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"name": "Widget", "price": 9.99}'
# Update (PATCH)
curl -X PATCH https://your-project.omnibase.live/rest/v1/products?id=eq.1 \
-H "apikey: YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"price": 12.99}'
# Upsert (insert or update)
curl -X POST https://your-project.omnibase.live/rest/v1/products \
-H "apikey: YOUR_KEY" \
-H "Prefer: resolution=merge-duplicates" \
-H "Content-Type: application/json" \
-d '{"id": 1, "name": "Widget Pro", "price": 14.99}'💡 Your interactive API docs with live testing are available at /docs (Swagger UI).
GraphQL
Every table is also available through GraphQL via pg_graphql. The endpoint mirrors your database schema automatically.
Endpoint
https://your-project.omnibase.live/graphql/v1Query example
query {
productsCollection(filter: { in_stock: { eq: true } }, first: 10) {
edges {
node {
id
name
price
}
}
}
}Mutation example
mutation {
insertIntoProductsCollection(objects: [
{ name: "New Widget", price: 19.99 }
]) {
records {
id
name
}
}
}GraphQL respects the same RLS policies as the REST API. Use the same API keys for authentication.
Authentication
Built-in authentication supports email/password, magic links, one-time passwords (OTP), and OAuth providers. Powered by GoTrue.
Email and password
// Sign up
const { data, error } = await supabase.auth.signUp({
email: 'user@example.com',
password: 'securepassword123'
})
// Sign in
const { data, error } = await supabase.auth.signInWithPassword({
email: 'user@example.com',
password: 'securepassword123'
})
// Sign out
await supabase.auth.signOut()Magic links (passwordless)
const { error } = await supabase.auth.signInWithOtp({
email: 'user@example.com'
})
// User receives an email with a login linkOAuth providers
const { data, error } = await supabase.auth.signInWithOAuth({
provider: 'google', // or 'github', 'discord', 'apple', etc.
options: {
redirectTo: 'https://yourapp.com/callback'
}
})Configure OAuth providers in Studio → Authentication → Providers.
Get current user
// Get the logged-in user
const { data: { user } } = await supabase.auth.getUser()
// Listen for auth state changes
supabase.auth.onAuthStateChange((event, session) => {
console.log(event, session)
// SIGNED_IN, SIGNED_OUT, TOKEN_REFRESHED, etc.
})Using auth in RLS policies
-- auth.uid() returns the current user's ID
CREATE POLICY "Users see own data"
ON profiles FOR SELECT
USING (auth.uid() = id);
-- auth.jwt() returns the full JWT claims
CREATE POLICY "Admin access"
ON admin_data FOR ALL
USING (auth.jwt() ->> 'role' = 'admin');Storage
S3-compatible file storage powered by MinIO. Upload images, documents, videos — any file type. Files are served globally through Bunny CDN.
Creating buckets
Create buckets in Studio → Storage, or via the API. Buckets can be public (anyone can read) or private (requires auth).
Upload files
const { data, error } = await supabase.storage
.from('avatars')
.upload('user123/photo.jpg', file, {
contentType: 'image/jpeg',
upsert: true // overwrite if exists
})Download and get URLs
// Public URL (for public buckets)
const { data } = supabase.storage
.from('avatars')
.getPublicUrl('user123/photo.jpg')
// → https://cdn.omnibase.live/storage/v1/object/public/avatars/user123/photo.jpg
// Signed URL (for private buckets, expires)
const { data, error } = await supabase.storage
.from('private-docs')
.createSignedUrl('report.pdf', 3600) // expires in 1 hourList and delete
// List files in a folder
const { data, error } = await supabase.storage
.from('avatars')
.list('user123', { limit: 100 })
// Delete
const { error } = await supabase.storage
.from('avatars')
.remove(['user123/photo.jpg'])Image transforms
// Resize on the fly
const { data } = supabase.storage
.from('avatars')
.getPublicUrl('photo.jpg', {
transform: { width: 200, height: 200, resize: 'cover' }
})💡 Files in public buckets are automatically served through Bunny CDN (120+ edge locations) for fast global delivery.
Realtime
Subscribe to database changes, broadcast messages between clients, and track user presence — all over WebSockets.
Listen to database changes
// Subscribe to all inserts on the 'messages' table
const channel = supabase
.channel('messages-feed')
.on('postgres_changes',
{ event: 'INSERT', schema: 'public', table: 'messages' },
(payload) => {
console.log('New message:', payload.new)
}
)
.subscribe()
// Listen to specific rows
.on('postgres_changes',
{ event: 'UPDATE', schema: 'public', table: 'orders', filter: 'id=eq.42' },
(payload) => console.log('Order updated:', payload.new)
)Broadcast (client-to-client)
// Send a message to all subscribers
const channel = supabase.channel('room-1')
channel.on('broadcast', { event: 'cursor' }, (payload) => {
console.log('Cursor moved:', payload)
})
await channel.subscribe()
// Send
channel.send({
type: 'broadcast',
event: 'cursor',
payload: { x: 100, y: 200 }
})Presence (who's online)
const channel = supabase.channel('online-users')
channel.on('presence', { event: 'sync' }, () => {
const state = channel.presenceState()
console.log('Online:', Object.keys(state).length)
})
await channel.subscribe(async (status) => {
if (status === 'SUBSCRIBED') {
await channel.track({ user_id: 'abc', name: 'Alice' })
}
})Edge Functions
Server-side TypeScript/JavaScript functions that run on Deno. Use them for webhooks, scheduled tasks, third-party API calls, or any logic that shouldn't run on the client.
Creating a function
// supabase/functions/hello/index.ts
import { serve } from 'https://deno.land/std@0.168.0/http/server.ts'
serve(async (req) => {
const { name } = await req.json()
return new Response(
JSON.stringify({ message: `Hello, ${name}!` }),
{ headers: { 'Content-Type': 'application/json' } }
)
})Calling from your app
const { data, error } = await supabase.functions.invoke('hello', {
body: { name: 'World' }
})Using with database
// Inside an edge function, create a Supabase client
import { createClient } from 'https://esm.sh/@supabase/supabase-js@2'
const supabase = createClient(
Deno.env.get('SUPABASE_URL')!,
Deno.env.get('SUPABASE_SERVICE_ROLE_KEY')!
)
const { data } = await supabase.from('orders').select('*')Redis Cache
Every omnibase project includes a Redis (Valkey) instance for caching, session storage, and rate limit counters. No setup required.
Use cases
| Use case | Example |
|---|---|
| Application cache | Cache API responses, computed results |
| Session storage | Store user sessions server-side |
| Rate limiting | Count requests per IP per minute |
| Job queues | Simple pub/sub for background tasks |
| Leaderboards | Sorted sets for rankings |
Connecting
// Node.js
import Redis from 'ioredis'
const redis = new Redis(process.env.REDIS_URL)
// Set and get
await redis.set('user:123:name', 'Alice', 'EX', 3600) // expires in 1hr
const name = await redis.get('user:123:name')
// Counter
await redis.incr('page:views:homepage')
// JSON
await redis.set('config', JSON.stringify({ theme: 'dark' }))
const config = JSON.parse(await redis.get('config'))| Plan | Redis memory |
|---|---|
| Starter ($12/mo) | 50MB |
| Pro ($25/mo) | 256MB |
| Flex ($10+/mo) | 128MB |
| Dedicated add-on | 1GB ($7/mo) |
CDN & Edge Caching
All files and API responses are served through Bunny CDN with 120+ global edge locations. Average response time: ~24ms worldwide.
How it works
Storage files are automatically served through cdn.omnibase.live. No configuration needed.
API responses can be cached at the edge with smart invalidation — when data changes in PostgreSQL, a trigger automatically purges the CDN cache for affected endpoints.
Smart invalidation
Uses pg_net to call the Bunny CDN purge API on writes. When you INSERT, UPDATE, or DELETE a row, the relevant cached responses are purged automatically. Reads stay fast, data is never stale.
💡 Edge caching is included on all plans at no extra cost. Most platforms charge for this or don't offer it at all.
Rate Limiting
Built-in per-endpoint, per-IP rate limiting via Kong. Protects your APIs from abuse out of the box. No Cloudflare or custom middleware needed.
Default limits
| Endpoint | Limit |
|---|---|
| Authentication (/auth) | 30 requests/minute |
| REST API (/rest) | 100 requests/minute |
| GraphQL (/graphql) | 100 requests/minute |
| Storage (/storage) | 60 requests/minute |
| Edge Functions (/functions) | 60 requests/minute |
When a limit is exceeded, the API returns 429 Too Many Requests with a Retry-After header.
💡 Need custom limits? Contact support and we can adjust per-endpoint rates for your project.
Monitoring
Full server monitoring via Grafana with 7 pre-configured dashboard panels. Available at grafana.omnibase.live.
What's monitored
| Metric | Source |
|---|---|
| CPU usage | Prometheus + node-exporter |
| RAM usage | Prometheus + node-exporter |
| Disk usage and I/O | Prometheus + node-exporter |
| Network traffic | Prometheus + node-exporter |
| System load | Prometheus + node-exporter |
| Container health | Docker health checks |
| Service uptime | Uptime Kuma (9 monitors) |
Metrics are stored for 30 days. Uptime Kuma sends Telegram alerts when services go down.
Public status page: uptime.omnibase.live
Analytics
Privacy-friendly web analytics via Umami. GDPR-compliant, no cookies, no personal data collection.
Adding to your site
<!-- Add to your HTML head -->
<script
async
src="https://umami.omnibase.live/script.js"
data-website-id="YOUR_WEBSITE_ID"
></script>What you get
Page views, unique visitors, bounce rate, session duration, referrers, browser/OS/device breakdown, geographic distribution — all without cookies or consent banners.
Dashboard: umami.omnibase.live
💡 Unlike Google Analytics, Umami doesn't track users across sites, sell data, or require cookie consent. Fully GDPR compliant by design.
Backups
Automated daily database backups to Bunny Storage, geo-replicated to Frankfurt and London. Runs at 4:00 AM UTC daily.
| Plan | Backup retention |
|---|---|
| Starter ($12/mo) | 7 days |
| Pro ($25/mo) | 30 days |
| Flex ($10+/mo) | 14 days |
Restore
Contact support for one-click restore from any backup point within your retention window. Restore typically takes 2-5 minutes depending on database size.
💡 Most platforms charge extra for daily backups. On omnibase, they're included on every plan.
Push Notifications
Real-time push notifications via self-hosted ntfy.sh. Send alerts to mobile and desktop without third-party services.
Free tier (all plans)
Service outage alerts, backup status notifications, and usage warnings (at 80% of limits).
Advanced ($5/mo add-on)
Custom triggers: new user signups, webhook failures, database events, and any custom condition you define.
Sending a notification
# From your app or server
curl -X POST https://ntfy.omnibase.live/your-topic \
-H "Title: New Order" \
-H "Priority: high" \
-H "Tags: shopping_cart" \
-d "Order #1234 received — $49.99"Subscribe on your phone with the ntfy app (iOS / Android) or any HTTP client.
Workflow Automation
Visual workflow automation via n8n with 400+ integrations. Build automations without code — connect your database to Slack, email, CRMs, payment processors, and more.
Availability
| Plan | n8n access |
|---|---|
| Starter ($12/mo) | $10/mo add-on |
| Pro ($25/mo) | Included |
| Flex ($10+/mo) | $10/mo add-on |
Example workflows
New row in database → send Slack notification. New user signup → add to Mailchimp list. Daily at 9am → generate report and email CSV. Stripe webhook → update subscription status in database.
Dashboard: n8n.omnibase.live
💡 n8n replaces Zapier ($20+/mo), Make, and similar tools. Each client gets their own isolated container.
AI Gateway
Unified proxy for OpenAI, Anthropic, and DeepSeek APIs with built-in Redis caching, cost tracking, and rate limiting. You bring your own API keys — zero AI cost risk for us.
How it works
// Instead of calling OpenAI directly:
// POST https://api.openai.com/v1/chat/completions
// Call through the omnibase AI Gateway:
POST https://ai.omnibase.live/v1/chat/completions
Authorization: Bearer YOUR_OPENAI_KEYBenefits
Response caching (identical prompts return cached results instantly), per-key cost tracking in PostgreSQL, rate limiting per client, and a single endpoint that routes to multiple providers.
| Plan | AI Gateway |
|---|---|
| All plans | $5-10/mo add-on |
Postgres Extensions
10 PostgreSQL extensions pre-installed and ready to use. No setup required.
| Extension | Purpose |
|---|---|
| pgvector | Vector embeddings for AI/ML similarity search |
| pgmq | Lightweight message queue (pub/sub) inside Postgres |
| pg_cron | Schedule recurring SQL jobs (like cron, but in the database) |
| PostGIS | Geospatial queries — distances, polygons, GPS coordinates |
| pg_stat_statements | Query performance stats — find slow queries |
| pgsodium | Encryption functions — encrypt columns, generate keys |
| pg_jsonschema | Validate JSON columns against JSON Schema |
| pg_hashids | Generate short, YouTube-like IDs from integers |
| pgaudit | Audit logging — log which RLS policies fire on writes |
| pg_net | Make HTTP requests from inside Postgres (used for CDN cache purging) |
Example: Vector search with pgvector
-- Create a table with embeddings
CREATE TABLE documents (
id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
content text,
embedding vector(1536) -- OpenAI ada-002 dimensions
);
-- Find similar documents
SELECT content, 1 - (embedding <=> '[0.1, 0.2, ...]') as similarity
FROM documents
ORDER BY embedding <=> '[0.1, 0.2, ...]'
LIMIT 5;Example: Scheduled jobs with pg_cron
-- Delete expired sessions every hour
SELECT cron.schedule('clean-sessions', '0 * * * *',
$$DELETE FROM sessions WHERE expires_at < now()$$
);
-- Generate daily report at midnight
SELECT cron.schedule('daily-report', '0 0 * * *',
$$INSERT INTO reports (date, total_users)
SELECT current_date, count(*) FROM auth.users$$
);Example: Message queue with pgmq
-- Create a queue
SELECT pgmq.create('email_queue');
-- Send a message
SELECT pgmq.send('email_queue', '{"to": "user@example.com", "subject": "Welcome!"}');
-- Read and process (locks message for 30 seconds)
SELECT * FROM pgmq.read('email_queue', 30, 1);
-- Delete after processing
SELECT pgmq.delete('email_queue', msg_id);Feature Flags
Simple key-value feature toggles stored in your PostgreSQL database. No third-party service needed.
Setting flags
-- In Studio or via SQL
INSERT INTO feature_flags (key, enabled, description)
VALUES ('dark_mode', true, 'Enable dark mode for all users');
-- Update
UPDATE feature_flags SET enabled = false WHERE key = 'dark_mode';Checking in your app
const { data } = await supabase
.from('feature_flags')
.select('enabled')
.eq('key', 'dark_mode')
.single()
if (data?.enabled) {
// Show dark mode
}💡 The feature_flags table has anon read access and service_role write access by default. Your frontend can check flags, but only your backend can change them.
Webhook Logs
Every outgoing webhook call is logged with status code, response time, retry count, and payload. Useful for debugging integrations.
Querying logs
// Find failed webhooks in the last 24 hours
const { data } = await supabase
.from('webhook_logs')
.select('*')
.neq('status', 200)
.gte('created_at', new Date(Date.now() - 86400000).toISOString())
.order('created_at', { ascending: false })The table is indexed by status and created_at for fast filtering.
Custom Domains
Use your own domain instead of the default omnibase subdomain. Available on Pro and Flex plans.
Setup
1. Add a CNAME record pointing to omnibase.live
2. Go to Settings → Custom Domain in your dashboard
3. Enter your domain and verify
SSL is automatically provisioned via Caddy (Let's Encrypt). Your API, auth, and storage endpoints all work under your custom domain.
Migrate from Supabase
omnibase uses the same open-source stack as Supabase. Migration is straightforward — your code, queries, and client libraries work without changes.
Step 1: Export your database
# From your Supabase project
pg_dump -h db.YOUR_PROJECT.supabase.co -U postgres -d postgres \
--no-owner --no-acl > backup.sqlStep 2: Import to omnibase
# Into your omnibase database
psql -h your-project.omnibase.live -U postgres -d postgres < backup.sqlStep 3: Update your app
// Change two lines in your app:
const supabase = createClient(
'https://your-project.omnibase.live', // was: https://xxx.supabase.co
'your-new-anon-key' // from your omnibase dashboard
)Step 4: Migrate storage
Download files from Supabase Storage and re-upload to your omnibase bucket. We can help with bulk migration — contact support.
💡 Moving from Supabase? First 3 months at 50% off. We'll help you migrate for free.
FAQ
Will my project be paused for inactivity?
No. Unlike Supabase's free tier which pauses after 7 days of inactivity, omnibase never pauses your project on any plan.
Can I get unexpected bills?
No. Fixed plans have hard limits with no overages. Flex plans have a spending cap you set — you'll never be charged more than your cap.
Where is my data stored?
Hetzner data center in Germany (EU). Your data is subject to EU jurisdiction only — no US CLOUD Act exposure.
Is it compatible with Supabase client libraries?
Yes, 100%. Use the same @supabase/supabase-js, Python, Dart, or any other Supabase client. Just change the URL and key.
What happens when I hit my plan limit?
The API returns a clear 429 error with a message to upgrade. You get email warnings at 80% and 95% of any limit. No surprise charges, no data loss.
Do you support SOC2/ISO 27001?
Not yet (those certifications require scale to justify the cost). However, your data is encrypted at rest and in transit, backups are geo-replicated, and we use industry-standard security practices.
Can I access my database directly?
Yes. You get full PostgreSQL access — connect with psql, pgAdmin, DBeaver, or any SQL tool. Connection details are in Settings → Database.
What if omnibase goes down?
Your data is always yours. Daily backups are stored on external geo-replicated storage. In a worst case, you can restore to any PostgreSQL host. We're built on open source — no vendor lock-in.
Limits & Quotas
| Resource | Starter ($12/mo) | Pro ($25/mo) | Flex ($10+/mo) |
|---|---|---|---|
| Database size | 5 GB | 20 GB | 2 GB + $1/GB |
| File storage | 10 GB | 50 GB | 5 GB + $0.50/GB |
| Bandwidth | 100 GB | 500 GB | 30 GB + $0.10/GB |
| Auth users | 10,000 | Unlimited | Unlimited |
| Realtime connections | 100 | 500 | 200 |
| Redis cache | 50 MB | 256 MB | 128 MB |
| pgmq queues | 3 | Unlimited | Unlimited |
| Backup retention | 7 days | 30 days | 14 days |
| Custom domain | — | ✓ | ✓ |
| n8n automation | $10/mo add-on | Included | $10/mo add-on |
| Support | Email (48hr) | Priority (24hr) | Email (48hr) |
API rate limits
| Endpoint | Requests per minute |
|---|---|
| Auth | 30 |
| REST API | 100 |
| GraphQL | 100 |
| Storage | 60 |
| Edge Functions | 60 |
At the limit
Fixed plans: hard block with clear error. Flex plans: hard block at spending cap. Both: email warnings at 80% and dashboard indicators at 80% (yellow) and 95% (red).
Need help? Email support@omnibase.live
© 2026 Omnium Labs Ltd. All rights reserved.