PocketBase: Backend in a Single File
Lightweight backend with admin UI. SQLite database, auth, and file storage.
Introduction
PocketBase is a revolutionary open-source backend that packages everything you need into a single executable file: database, authentication, file storage, real-time subscriptions, and an admin dashboard. No complex setup, no dependency management, no microservices architecture - just one file that runs anywhere.
What makes PocketBase unique?
- Single binary - Download one file and run it, no installation needed
- SQLite database - Fast, reliable, embedded database with full SQL support
- Built-in auth - Email/password, OAuth2, JWT tokens out of the box
- File storage - Upload and serve files with automatic image resizing
- Real-time subscriptions - WebSocket support for live data updates
- Admin UI - Beautiful web-based dashboard for managing everything
- REST & Realtime APIs - Auto-generated APIs from your schema
- Go-powered - Written in Go for exceptional performance and low resource usage
Perfect for personal projects, internal tools, MVPs, and homelab applications where you want a powerful backend without the complexity of traditional setups.
Prerequisites
Before starting, you should have:
- Basic command line knowledge - Ability to run commands and edit files
- System resources - Minimal requirements: 256MB RAM, 100MB disk space
- Optional: Docker - For containerized deployments
- Optional: Domain name - For production with SSL
Nice to have:
- Understanding of REST APIs
- Basic SQL knowledge
- JavaScript/TypeScript experience for client integration
Understanding PocketBase Architecture
PocketBase is beautifully simple compared to traditional backends:
Traditional Backend Stack
Frontend
↓
API Server (Node.js/Python/Go)
↓
Authentication Service
↓
Database Server (PostgreSQL/MySQL)
↓
File Storage Service (S3/MinIO)
↓
Cache Layer (Redis) PocketBase Stack
Frontend
↓
PocketBase (Everything in one binary)
- SQLite Database
- Authentication
- File Storage
- Real-time Subscriptions
- Admin Dashboard Core Components:
Database (SQLite)
- Embedded database, no separate server needed
- Full SQL support with relations
- ACID compliant, battle-tested reliability
- Stored in a single
pb_data/data.dbfile
Authentication System
- Email/password with verification
- OAuth2 (Google, GitHub, Facebook, etc.)
- JWT tokens and refresh tokens
- User and admin roles
File Storage
- Stores files in
pb_data/storage - Automatic image resizing and thumbnails
- Direct file serving with optional auth
- Configurable size limits
Real-time Engine
- WebSocket-based subscriptions
- Automatic updates when data changes
- Per-record or collection-wide subscriptions
- Built-in presence tracking
Admin Dashboard
- Web UI at
/admin - Visual schema builder
- User management
- API rules editor
- Logs and analytics
Installation and Setup
Method 1: Binary Installation (Simplest)
Linux/macOS:
# Create project directory
mkdir pocketbase-homelab
cd pocketbase-homelab
# Download PocketBase (check https://pocketbase.io/docs/ for latest version)
wget https://github.com/pocketbase/pocketbase/releases/download/v0.22.0/pocketbase_0.22.0_linux_amd64.zip
# Extract
unzip pocketbase_0.22.0_linux_amd64.zip
# Make executable
chmod +x pocketbase
# Run PocketBase
./pocketbase serve
# Access admin UI at http://127.0.0.1:8090/_/ First run setup:
- Navigate to
http://127.0.0.1:8090/_/ - Create your admin account (email + password)
- You’re ready to build!
Custom configuration:
# Run on different port
./pocketbase serve --http=0.0.0.0:8080
# Specify data directory
./pocketbase serve --dir=/path/to/pb_data
# Enable debug mode
./pocketbase serve --dev
# Show help
./pocketbase --help Method 2: Docker Deployment
Create docker-compose.yml:
version: '3.8'
services:
pocketbase:
image: ghcr.io/muchobien/pocketbase:latest
container_name: pocketbase
restart: unless-stopped
ports:
- "8090:8090"
volumes:
- pocketbase_data:/pb/pb_data
- pocketbase_public:/pb/pb_public
environment:
- TZ=America/New_York
healthcheck:
test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost:8090/api/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 10s
volumes:
pocketbase_data:
pocketbase_public: Deploy:
# Start PocketBase
docker compose up -d
# View logs
docker compose logs -f pocketbase
# Access at http://localhost:8090/_/ Method 3: Systemd Service (Production)
Create service file /etc/systemd/system/pocketbase.service:
[Unit]
Description=PocketBase Backend Service
After=network.target
[Service]
Type=simple
User=pocketbase
Group=pocketbase
WorkingDirectory=/opt/pocketbase
ExecStart=/opt/pocketbase/pocketbase serve --http=0.0.0.0:8090
Restart=always
RestartSec=5
# Security hardening
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict
ProtectHome=true
ReadWritePaths=/opt/pocketbase/pb_data
[Install]
WantedBy=multi-user.target Setup and start:
# Create user and directory
sudo useradd -r -s /bin/false pocketbase
sudo mkdir -p /opt/pocketbase
sudo chown pocketbase:pocketbase /opt/pocketbase
# Copy pocketbase binary
sudo cp pocketbase /opt/pocketbase/
sudo chown pocketbase:pocketbase /opt/pocketbase/pocketbase
# Enable and start service
sudo systemctl daemon-reload
sudo systemctl enable pocketbase
sudo systemctl start pocketbase
# Check status
sudo systemctl status pocketbase Database Configuration
Creating Collections (Tables)
Collections in PocketBase are like tables in traditional databases.
Using Admin UI:
- Go to Admin Dashboard > Collections
- Click “New Collection”
- Choose collection type:
- Base - Standard data collection
- Auth - User authentication collection
- View - SQL view (read-only)
Example: Creating a Blog Posts collection
Collection settings:
- Name:
posts - Type: Base
- Schema:
| Field | Type | Required | Options |
|---|---|---|---|
| title | Text | Yes | Min: 3, Max: 200 |
| content | Editor | Yes | - |
| excerpt | Text | No | Max: 500 |
| featured_image | File | No | Max files: 1 |
| published | Bool | Yes | Default: false |
| author | Relation | Yes | Collection: users |
| tags | JSON | No | - |
Using the API:
// Create collection via API (admin auth required)
const collection = await pb.collections.create({
name: 'posts',
type: 'base',
schema: [
{
name: 'title',
type: 'text',
required: true,
options: { min: 3, max: 200 }
},
{
name: 'content',
type: 'editor',
required: true
},
{
name: 'published',
type: 'bool',
required: true
},
{
name: 'author',
type: 'relation',
required: true,
options: { collectionId: 'users_id', maxSelect: 1 }
}
]
}) Schema Field Types
Text Fields:
text- Short text (URL, plain editor)editor- Rich text editoremail- Email validationurl- URL validation
Number Fields:
number- Integer or floatbool- True/false checkbox
Date/Time:
date- Date picker
Relations:
relation- Link to records in other collectionsselect- Dropdown with predefined values
Files:
file- File/image upload with size limits
Advanced:
json- Store JSON dataautodate- Automatically set creation/update time
API Rules and Security
API Rules control access to your collections. Each collection has rules for:
- List/Search - Who can query records
- View - Who can view a single record
- Create - Who can create records
- Update - Who can update records
- Delete - Who can delete records
Rule syntax examples:
// Public read access
@request.auth.id != "" // Any authenticated user
// Owner only
@request.auth.id = author.id // Author field matches current user
// Public published posts
published = true // Anyone can read if published
// Admin only
@request.auth.role = "admin"
// Combined rules
published = true || @request.auth.id = author.id // Public if published OR owner
// Check relation
@request.auth.id = comments.author.id // User owns related comment Example: Blog post rules
List/Search rule:
published = true || @request.auth.id = author.id
View rule:
published = true || @request.auth.id = author.id
Create rule:
@request.auth.id != "" && @request.data.author = @request.auth.id
Update rule:
@request.auth.id = author.id
Delete rule:
@request.auth.id = author.id || @request.auth.role = "admin" Database Migrations
Export schema for version control:
# Export all collections
./pocketbase export collections
# Creates JSON file with schema Import schema:
# Import collections
./pocketbase import collections schema.json Authentication Setup
Creating Auth Collection
By default, PocketBase includes a users auth collection. You can create additional auth collections for different user types.
Default users collection fields:
email- Unique email addressverified- Email verification statusemailVisibility- Show email in API responsesusername- Optional unique usernamename- Display nameavatar- Profile picture
Add custom fields:
// Via admin UI: Collections > users > Edit
// Add fields like:
- bio (Text)
- location (Text)
- website (URL)
- social_links (JSON) Email/Password Authentication
JavaScript/TypeScript client:
npm install pocketbase import PocketBase from 'pocketbase'
const pb = new PocketBase('http://127.0.0.1:8090')
// Sign up new user
const user = await pb.collection('users').create({
email: '[email protected]',
password: 'secure-password',
passwordConfirm: 'secure-password',
name: 'John Doe',
// Add custom fields
bio: 'Software developer and homelab enthusiast'
})
// Send verification email
await pb.collection('users').requestVerification('[email protected]')
// Sign in
const authData = await pb.collection('users').authWithPassword(
'[email protected]',
'secure-password'
)
// Access token
console.log(pb.authStore.token)
console.log(pb.authStore.model) // User data
// Check if authenticated
console.log(pb.authStore.isValid)
// Sign out
pb.authStore.clear() Auto-refresh authentication:
// Enable auto refresh (default: enabled)
pb.autoCancellation(false)
// Manual refresh
await pb.collection('users').authRefresh()
// Listen to auth state changes
pb.authStore.onChange((token, model) => {
console.log('Auth state changed:', token, model)
}) OAuth2 Authentication
Enable OAuth providers in Admin UI:
- Settings > Auth providers
- Enable provider (Google, GitHub, Facebook, etc.)
- Enter Client ID and Client Secret
- Set redirect URL
Use in your app:
// List available OAuth providers
const authMethods = await pb.collection('users').listAuthMethods()
// Initiate OAuth flow
const authData = await pb.collection('users').authWithOAuth2({
provider: 'google',
createData: {
name: 'John Doe',
// Additional user data
}
})
// GitHub OAuth example
const authData = await pb.collection('users').authWithOAuth2({
provider: 'github',
urlCallback: (url) => {
// Open OAuth URL in popup or redirect
window.open(url)
}
}) Password Reset
// Request password reset email
await pb.collection('users').requestPasswordReset('[email protected]')
// Reset password with token (from email link)
await pb.collection('users').confirmPasswordReset(
'TOKEN_FROM_EMAIL',
'new-password',
'new-password'
) API Usage and Examples
CRUD Operations
Create record:
const record = await pb.collection('posts').create({
title: 'My First Post',
content: 'This is the content of my first post',
published: true,
author: pb.authStore.model.id,
tags: ['tutorial', 'homelab']
}) List records:
// Get all posts
const records = await pb.collection('posts').getFullList({
sort: '-created',
})
// Paginated list
const resultList = await pb.collection('posts').getList(1, 20, {
filter: 'published = true',
sort: '-created',
expand: 'author', // Include related author data
})
console.log(resultList.page)
console.log(resultList.perPage)
console.log(resultList.totalItems)
console.log(resultList.items) Get single record:
const record = await pb.collection('posts').getOne('RECORD_ID', {
expand: 'author',
}) Update record:
const record = await pb.collection('posts').update('RECORD_ID', {
title: 'Updated Title',
published: true,
}) Delete record:
await pb.collection('posts').delete('RECORD_ID') Advanced Filtering
// Text search
filter: 'title ~ "homelab"' // Contains "homelab"
// Exact match
filter: 'published = true'
// Comparison
filter: 'created >= "2025-01-01"'
// Multiple conditions
filter: 'published = true && author = "USER_ID"'
// OR conditions
filter: 'published = true || author = "USER_ID"'
// Arrays (JSON fields)
filter: 'tags:each ~ "tutorial"'
// Relations
filter: 'author.name ~ "John"'
// Combining filters
const posts = await pb.collection('posts').getFullList({
filter: 'published = true && tags:each ~ "homelab"',
sort: '-created',
expand: 'author',
}) Real-time Subscriptions
Subscribe to collection changes:
// Subscribe to all changes in posts collection
pb.collection('posts').subscribe('*', (e) => {
console.log('Action:', e.action) // create, update, delete
console.log('Record:', e.record)
})
// Subscribe to specific record
pb.collection('posts').subscribe('RECORD_ID', (e) => {
console.log('Post updated:', e.record)
})
// Subscribe with filter
pb.collection('posts').subscribe('*', (e) => {
console.log('New published post:', e.record)
}, {
filter: 'published = true'
})
// Unsubscribe
pb.collection('posts').unsubscribe('*')
pb.collection('posts').unsubscribe('RECORD_ID') React example with real-time:
import { useEffect, useState } from 'react'
import PocketBase from 'pocketbase'
const pb = new PocketBase('http://127.0.0.1:8090')
function PostsList() {
const [posts, setPosts] = useState([])
useEffect(() => {
// Initial fetch
const fetchPosts = async () => {
const records = await pb.collection('posts').getFullList({
sort: '-created',
expand: 'author',
})
setPosts(records)
}
fetchPosts()
// Subscribe to real-time updates
pb.collection('posts').subscribe('*', (e) => {
if (e.action === 'create') {
setPosts(prev => [e.record, ...prev])
} else if (e.action === 'update') {
setPosts(prev => prev.map(p => p.id === e.record.id ? e.record : p))
} else if (e.action === 'delete') {
setPosts(prev => prev.filter(p => p.id !== e.record.id))
}
})
// Cleanup
return () => {
pb.collection('posts').unsubscribe('*')
}
}, [])
return (
<div>
{posts.map(post => (
<article key={post.id}>
<h2>{post.title}</h2>
<p>{post.content}</p>
<small>By {post.expand?.author?.name}</small>
</article>
))}
</div>
)
} File Uploads
Upload files:
// Create FormData
const formData = new FormData()
formData.append('title', 'Post with Image')
formData.append('content', 'Content here')
formData.append('featured_image', fileInput.files[0])
formData.append('author', pb.authStore.model.id)
// Create record with file
const record = await pb.collection('posts').create(formData)
// Update with new file
formData.append('featured_image', newFile)
await pb.collection('posts').update(record.id, formData) Get file URL:
// Get file URL
const imageUrl = pb.files.getUrl(record, record.featured_image)
// With thumbnail (for images)
const thumbUrl = pb.files.getUrl(record, record.featured_image, {
thumb: '100x100'
})
// Available thumb sizes: 100x100, 200x200, 300x300, etc. Delete file:
// Remove file from record
await pb.collection('posts').update(record.id, {
featured_image: null
}) Example Integrations
Next.js Integration
Setup PocketBase client (lib/pocketbase.ts):
import PocketBase from 'pocketbase'
export const pb = new PocketBase('http://localhost:8090')
// Enable auto-cancellation (optional)
pb.autoCancellation(false)
// Load auth from cookies (SSR)
export function initPocketBase(req?: any) {
if (typeof window === 'undefined' && req) {
// Server-side: load auth from cookies
pb.authStore.loadFromCookie(req.headers.cookie || '')
}
return pb
} API route (pages/api/posts.ts):
import type { NextApiRequest, NextApiResponse } from 'next'
import { initPocketBase } from '@/lib/pocketbase'
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
const pb = initPocketBase(req)
if (req.method === 'GET') {
const posts = await pb.collection('posts').getFullList({
sort: '-created',
filter: 'published = true',
expand: 'author',
})
res.status(200).json(posts)
} else if (req.method === 'POST') {
const post = await pb.collection('posts').create(req.body)
res.status(201).json(post)
} else {
res.status(405).json({ error: 'Method not allowed' })
}
} Python Integration
pip install pocketbase from pocketbase import PocketBase
# Initialize client
pb = PocketBase('http://127.0.0.1:8090')
# Authenticate
admin_data = pb.admins.auth_with_password('[email protected]', 'password')
# Or user auth
user_data = pb.collection('users').auth_with_password('[email protected]', 'password')
# Create record
post = pb.collection('posts').create({
'title': 'New Post from Python',
'content': 'Content here',
'published': True,
'author': pb.auth_store.model.id
})
# Get records
posts = pb.collection('posts').get_full_list(
query_params={
'filter': 'published = true',
'sort': '-created',
'expand': 'author'
}
)
# Update record
updated_post = pb.collection('posts').update(post.id, {
'title': 'Updated Title'
})
# Delete record
pb.collection('posts').delete(post.id)
# File upload
with open('image.jpg', 'rb') as f:
post_with_file = pb.collection('posts').create({
'title': 'Post with Image',
'featured_image': f
}) Vue.js Integration
<script setup>
import { ref, onMounted, onUnmounted } from 'vue'
import PocketBase from 'pocketbase'
const pb = new PocketBase('http://127.0.0.1:8090')
const posts = ref([])
const isAuthenticated = ref(pb.authStore.isValid)
async function login(email, password) {
await pb.collection('users').authWithPassword(email, password)
isAuthenticated.value = true
}
async function createPost(title, content) {
const post = await pb.collection('posts').create({
title,
content,
published: true,
author: pb.authStore.model.id
})
posts.value.unshift(post)
}
onMounted(async () => {
// Fetch initial posts
posts.value = await pb.collection('posts').getFullList({
sort: '-created',
filter: 'published = true',
expand: 'author'
})
// Subscribe to real-time updates
pb.collection('posts').subscribe('*', (e) => {
if (e.action === 'create') {
posts.value.unshift(e.record)
} else if (e.action === 'update') {
const index = posts.value.findIndex(p => p.id === e.record.id)
if (index !== -1) posts.value[index] = e.record
} else if (e.action === 'delete') {
posts.value = posts.value.filter(p => p.id !== e.record.id)
}
})
})
onUnmounted(() => {
pb.collection('posts').unsubscribe('*')
})
</script>
<template>
<div>
<div v-for="post in posts" :key="post.id">
<h2>{{ post.title }}</h2>
<p>{{ post.content }}</p>
</div>
</div>
</template> Backup Procedures
Manual Backup
PocketBase stores all data in the pb_data directory:
# Stop PocketBase (if running as service)
sudo systemctl stop pocketbase
# Create backup
tar czf pocketbase-backup-$(date +%Y%m%d).tar.gz pb_data/
# Restart PocketBase
sudo systemctl start pocketbase Backup contains:
data.db- SQLite databasestorage/- Uploaded fileslogs.db- Application logs
Automated Backup Script
#!/bin/bash
# backup-pocketbase.sh
POCKETBASE_DIR="/opt/pocketbase"
BACKUP_DIR="/backups/pocketbase"
DATE=$(date +%Y%m%d_%H%M%S)
RETENTION_DAYS=30
# Create backup directory
mkdir -p $BACKUP_DIR
# Backup database (online backup using SQLite)
sqlite3 $POCKETBASE_DIR/pb_data/data.db ".backup '$BACKUP_DIR/data_$DATE.db'"
# Backup entire pb_data directory
tar czf "$BACKUP_DIR/pb_data_$DATE.tar.gz" -C $POCKETBASE_DIR pb_data/
# Remove old backups
find $BACKUP_DIR -name "data_*.db" -mtime +$RETENTION_DAYS -delete
find $BACKUP_DIR -name "pb_data_*.tar.gz" -mtime +$RETENTION_DAYS -delete
echo "Backup completed: $DATE" Schedule with cron:
# Run daily at 3 AM
0 3 * * * /home/user/backup-pocketbase.sh >> /var/log/pocketbase-backup.log 2>&1 Restore from Backup
# Stop PocketBase
sudo systemctl stop pocketbase
# Restore from backup
cd /opt/pocketbase
rm -rf pb_data/ # WARNING: This deletes current data
tar xzf /backups/pocketbase/pb_data_20250115.tar.gz
# Start PocketBase
sudo systemctl start pocketbase Production Deployment
Reverse Proxy with nginx
server {
listen 443 ssl http2;
server_name api.yourdomain.com;
ssl_certificate /etc/letsencrypt/live/api.yourdomain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/api.yourdomain.com/privkey.pem;
# Increase upload size limit
client_max_body_size 50M;
location / {
proxy_pass http://127.0.0.1:8090;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# WebSocket support for real-time
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
# Timeouts
proxy_connect_timeout 600;
proxy_send_timeout 600;
proxy_read_timeout 600;
send_timeout 600;
}
}
# Redirect HTTP to HTTPS
server {
listen 80;
server_name api.yourdomain.com;
return 301 https://$server_name$request_uri;
} Traefik Configuration
# docker-compose.yml with Traefik
version: '3.8'
services:
pocketbase:
image: ghcr.io/muchobien/pocketbase:latest
container_name: pocketbase
restart: unless-stopped
volumes:
- pocketbase_data:/pb/pb_data
networks:
- traefik
labels:
- "traefik.enable=true"
- "traefik.http.routers.pocketbase.rule=Host(`api.yourdomain.com`)"
- "traefik.http.routers.pocketbase.entrypoints=websecure"
- "traefik.http.routers.pocketbase.tls.certresolver=letsencrypt"
- "traefik.http.services.pocketbase.loadbalancer.server.port=8090"
volumes:
pocketbase_data:
networks:
traefik:
external: true Environment Variables
Configure PocketBase via environment variables:
# .env file
POCKETBASE_PORT=8090
POCKETBASE_DATA_DIR=/pb/pb_data
POCKETBASE_ENCRYPTION_KEY=your-32-char-encryption-key Troubleshooting
Database Locked Error
Symptoms: database is locked error when multiple requests occur
Solutions:
# Check for zombie processes
ps aux | grep pocketbase
# Increase SQLite timeout in custom build
# Or use WAL mode (enabled by default in recent versions) Enable WAL mode manually:
sqlite3 pb_data/data.db "PRAGMA journal_mode=WAL;" File Upload Fails
Check file size limits:
Admin UI > Settings > File upload > Max file size
Verify storage permissions:
# Check permissions
ls -la pb_data/storage/
# Fix permissions
chmod -R 755 pb_data/storage/ Authentication Issues
JWT token expired:
// Force refresh token
await pb.collection('users').authRefresh()
// Check expiry
console.log(pb.authStore.token)
console.log(pb.authStore.isValid) Clear auth state:
pb.authStore.clear() Real-time Not Working
Check WebSocket connection:
- Ensure reverse proxy supports WebSocket upgrades
- Verify firewall allows WebSocket connections
- Check browser console for connection errors
Test connection:
pb.realtime.subscribe('test', (data) => {
console.log('Real-time connected:', data)
}) Performance Optimization
SQLite optimization:
-- Analyze and optimize database
sqlite3 pb_data/data.db "PRAGMA optimize;"
sqlite3 pb_data/data.db "VACUUM;"
-- Create indexes for common queries
sqlite3 pb_data/data.db "CREATE INDEX idx_posts_created ON posts(created DESC);" Monitor database size:
# Check database size
du -h pb_data/data.db
# Check storage size
du -h pb_data/storage/ Advanced Features
Custom Routes (Hooks)
Extend PocketBase with custom Go code:
// main.go
package main
import (
"log"
"github.com/pocketbase/pocketbase"
"github.com/pocketbase/pocketbase/core"
)
func main() {
app := pocketbase.New()
// Custom API endpoint
app.OnBeforeServe().Add(func(e *core.ServeEvent) error {
e.Router.GET("/api/custom", func(c echo.Context) error {
return c.JSON(200, map[string]string{
"message": "Custom endpoint",
})
})
return nil
})
if err := app.Start(); err != nil {
log.Fatal(err)
}
} Database Views
Create read-only SQL views:
-- Via admin UI: Collections > New Collection > View
-- Example: Recent posts view
SELECT
p.id,
p.title,
p.content,
p.created,
u.name as author_name,
u.avatar as author_avatar
FROM posts p
LEFT JOIN users u ON p.author = u.id
WHERE p.published = true
ORDER BY p.created DESC
LIMIT 10 SMTP Configuration
Admin UI: Settings > Mail settings
SMTP Host: smtp.gmail.com
SMTP Port: 587
SMTP Username: [email protected]
SMTP Password: your-app-password
Sender Name: PocketBase Homelab
Sender Email: [email protected] Test email:
// Request verification email
await pb.collection('users').requestVerification('[email protected]') Conclusion
PocketBase proves that powerful doesn’t have to mean complex. With a single binary, you get a complete backend that’s perfect for homelab projects, internal tools, MVPs, and production applications. Its simplicity makes it incredibly easy to deploy and maintain, while SQLite ensures reliability and performance.
Key advantages:
- Zero complexity - One file, no dependencies
- Batteries included - Auth, DB, storage, real-time, admin UI
- Lightning fast - Go-powered performance
- Self-contained - SQLite means no external database
- Developer friendly - Excellent documentation and SDKs
Perfect for:
- Personal projects and MVPs
- Internal homelab tools
- Mobile app backends
- Rapid prototyping
- Educational projects
Next steps:
- Explore the PocketBase documentation
- Join the Discord community
- Check out example projects on GitHub
- Build your first app with PocketBase
Start simple, scale as needed, and enjoy the freedom of a backend that just works!