Deploying Dropserver
Introduction
Dropserver is an open-source application platform designed to host personal web services for individuals, families, small teams, and clubs. Unlike traditional platforms that scale applications to serve thousands of users, Dropserver focuses on running multiple small-scale applications for small groups of trusted users. Built in Go with Deno-powered sandboxing, Dropserver provides a secure, efficient environment for self-hosted web applications.
Dropserver is designed to be:
- Personal-Scale: Optimized for applications serving you, your family, or small teamsβnot thousands of users
- Multi-App Platform: Run dozens of different applications on a single instance
- Sandboxed & Secure: Each app runs in isolated Deno sandboxes with explicit permission controls
- User-Friendly: One-click app installation with no technical configuration required
- Privacy-First: Your data stays under your control on your infrastructure
- Resource-Efficient: Apps only consume resources when actively used
- Portable: Export apps and data to move between Dropserver instances
Key features include:
- Appspaces: Isolated instances of applications with their own data and users
- User Management: Built-in authentication system with avatars and access controls
- Deno Sandboxing: TypeScript/JavaScript applications run in secure Deno sandboxes
- Permission System: Granular control over filesystem, network, and API access
- Data Migration: Built-in hooks for upgrading app data schemas
- Static Asset Serving: Efficient delivery of frontend resources
- Public and Private Routes: Mix authenticated and public-facing endpoints
- Development Tools: ds-dev provides local development environment for building apps
This guide walks you through deploying Dropserver on Klutch.sh using Docker, including configuration for subdomain routing, persistent storage, Deno installation, and production-ready deployment practices.
Why Deploy Dropserver on Klutch.sh?
Klutch.sh provides an excellent platform for hosting Dropserver with several advantages:
- Automatic Docker Detection: Klutch.sh detects your Dockerfile and builds containers automatically
- Subdomain Support: Perfect for Dropserverβs subdomain-based appspace routing
- Built-in HTTPS: Automatic SSL certificates for main domain and all subdomains
- Persistent Volumes: Reliable storage for appspace data, user information, and uploads
- Environment Management: Securely configure domains, ports, and application settings
- Always Available: 24/7 uptime for your personal web services
- Scalable Resources: Adjust CPU/RAM as your appspace collection grows
- Simple Deployment: Push to GitHub and deployβno complex infrastructure management
Prerequisites
Before deploying Dropserver to Klutch.sh, ensure you have:
- A Klutch.sh account (free to get started)
- A GitHub account for repository hosting
- Docker installed locally for testing (recommended)
- A domain with wildcard subdomain support (*.yourdomain.com)
- Basic understanding of Docker, web applications, and DNS configuration
- Familiarity with TypeScript or JavaScript (for building custom apps)
Understanding Dropserver Architecture
Dropserver operates on a unique multi-tenant application platform model:
Core Components
ds-host: The main Dropserver executable
- Written in Go for performance and reliability
- Serves administrator UI, user authentication, and appspace requests
- Manages application lifecycle, user permissions, and data storage
- Handles TLS termination and subdomain routing
- Coordinates Deno sandboxes for app execution
Deno Runtime: Sandboxed execution environment
- Each app runs in isolated Deno sandbox
- TypeScript and JavaScript support
- Explicit permission model (filesystem, network, env access)
- Prevents apps from interfering with each other or the host system
- Lightweight and fast startup for on-demand execution
Appspaces: Isolated application instances
- Each appspace is a running instance of an installed application
- Has its own subdomain (e.g., leftovers.example.com)
- Maintains separate data storage and user access lists
- Multiple appspaces can run from the same app package
- Only consumes resources when actively handling requests
Application Model: How apps are structured
- Apps are TypeScript/JavaScript packages with a manifest
- Define routes (public and private), data schema, and permissions
- Access Dropserver APIs for user management, file storage, etc.
- Can be exported/imported between Dropserver instances
- Version controlled with migration hooks for data schema changes
Data Flow
- User navigates to subdomain (e.g., shopping.example.com)
- Dropserver routes request to corresponding appspace
- User authentication checked (if private route)
- Appspace Deno sandbox initialized (if not already running)
- App code handles request and generates response
- Static assets served efficiently without sandbox overhead
- Sandbox idles out after period of inactivity
Port and Domain Requirements
- HTTP/HTTPS: Port 3003 (customizable) for all traffic
- Subdomain Routing: Requires wildcard DNS (*.yourdomain.com)
- Admin Subdomain: Typically
dropid.yourdomain.comfor management - Appspace Subdomains: Each appspace gets unique subdomain
- TLS Required: Dropserver requires HTTPS in production
Installation and Setup
Step 1: Create Your Project Repository
Create a new directory for your Dropserver deployment:
mkdir dropserver-klutchcd dropserver-klutchgit initStep 2: Create the Dockerfile
Create a Dockerfile in your project root. Klutch.sh automatically detects and builds from this file:
# Build stage - Download and prepare DropserverFROM debian:bookworm-slim AS downloader
# Install dependencies for downloading and extractingRUN apt-get update && apt-get install -y \ curl \ ca-certificates \ unzip \ && rm -rf /var/lib/apt/lists/*
# Download latest Dropserver release (x86-64)WORKDIR /tmpRUN curl -L https://github.com/teleclimber/Dropserver/releases/download/v0.14.4/ds-host-v0.14.4-x86-64.zip -o dropserver.zip && \ unzip dropserver.zip && \ chmod +x ds-host
# Runtime stage - Minimal image with Deno and DropserverFROM debian:bookworm-slim
# Install runtime dependenciesRUN apt-get update && apt-get install -y \ ca-certificates \ curl \ unzip \ && rm -rf /var/lib/apt/lists/*
# Install Deno (required for app sandboxing)RUN curl -fsSL https://deno.land/install.sh | shENV DENO_INSTALL="/root/.deno"ENV PATH="$DENO_INSTALL/bin:$PATH"
# Copy Dropserver binary from build stageCOPY --from=downloader /tmp/ds-host /usr/local/bin/ds-host
# Create directories for data and socketsRUN mkdir -p /srv/dropserver /var/run/dropserver && \ chmod 755 /srv/dropserver /var/run/dropserver
# Create config directoryRUN mkdir -p /etc/dropserver
# Copy configuration file (we'll create this next)COPY config.json /etc/dropserver/config.json
WORKDIR /srv/dropserver
# Expose Dropserver portEXPOSE 3003
# Health checkHEALTHCHECK --interval=30s --timeout=5s --start-period=30s --retries=3 \ CMD curl -f http://localhost:3003/health || exit 1
# Start DropserverCMD ["ds-host", "-config=/etc/dropserver/config.json"]Dockerfile Explanation:
- Multi-stage build: Downloads Dropserver in separate stage for smaller final image
- Deno installation: Required for app sandboxing
- Data directories:
/srv/dropserverfor persistent data,/var/run/dropserverfor sockets - Port 3003: Default Dropserver HTTP port (customizable)
- Health check: Monitors server availability
Step 3: Create Configuration File
Create config.json in your project root for Dropserver configuration:
{ "data-dir": "/srv/dropserver", "server": { "http-port": 3003, "no-tls": true }, "external-access": { "scheme": "https", "domain": "example.com", "subdomain": "dropid" }, "sandbox": { "sockets-dir": "/var/run/dropserver", "use-bubblewrap": false, "use-cgroups": false, "memory-high-mb": 512 }}Configuration Explanation:
data-dir: Where Dropserver stores appspace data, user info, and uploadsserver.http-port: Internal port Dropserver listens onserver.no-tls: Set to true since Klutch.sh handles TLS terminationexternal-access.scheme: Alwayshttpsin production (Klutch.sh provides SSL)external-access.domain: Your custom domain (replaceexample.com)external-access.subdomain: Subdomain for admin interface (typicallydropid)sandbox.sockets-dir: Unix socket directory for sandbox communicationsandbox.use-cgroups: Set to false (cgroups require systemd delegation)sandbox.memory-high-mb: Memory limit for all sandboxes combined
Important: Replace example.com with your actual domain before deployment.
Step 4: Create Environment Variables Template
Create .env.example file documenting configuration options:
# Dropserver Environment Configuration
# Domain ConfigurationDROPSERVER_DOMAIN=example.comDROPSERVER_SUBDOMAIN=dropid
# Server ConfigurationDROPSERVER_PORT=3003DROPSERVER_DATA_DIR=/srv/dropserverDROPSERVER_SOCKETS_DIR=/var/run/dropserver
# Sandbox ConfigurationDROPSERVER_MEMORY_LIMIT_MB=512DROPSERVER_USE_CGROUPS=false
# External Access (Klutch.sh handles TLS)DROPSERVER_SCHEME=httpsDROPSERVER_NO_TLS=trueStep 5: Create .dockerignore File
Create .dockerignore to exclude unnecessary files from the build:
.git.github.gitignore.env.env.local*.mdREADME.mdnode_modules.DS_StoreStep 6: Add Documentation Files
Create README.md with deployment instructions:
# Dropserver on Klutch.sh
Personal web services application platform deployed on Klutch.sh.
## Features
- Sandboxed multi-app hosting- Built-in user management- Subdomain-based appspace routing- Deno-powered TypeScript/JavaScript apps- Data export and migration support- One-click app installation
## Deployment
This Dropserver instance is deployed on Klutch.sh with automatic Docker builds.
### Prerequisites
1. Domain with wildcard DNS configured (*.yourdomain.com)2. DNS A/AAAA records pointing to Klutch.sh infrastructure3. Klutch.sh account with custom domain configured
### Configuration
Update `config.json` before deployment:
- Set `external-access.domain` to your domain- Choose `external-access.subdomain` for admin interface- Adjust `sandbox.memory-high-mb` based on expected app usage
### Persistent Storage
Attach a persistent volume in Klutch.sh dashboard:
- **Mount Path**: `/srv/dropserver`- **Size**: 10GB minimum (scale based on number of appspaces)
### Initial Setup
1. Deploy to Klutch.sh2. Look for `setup_key_reveal=` in logs3. Use the secret link to create admin account4. Access admin interface at https://dropid.yourdomain.com5. Create your dropid (user identity)6. Install apps from URL or manual upload7. Create appspaces for installed apps
## Building Custom Apps
See <a href="https://dropserver.org/docs/build-dropserver-app-tutorial" target="_blank" rel="noopener noreferrer">Dropserver app tutorial</a> for building your own apps.
## License
Apache License Version 2.0 (Dropserver) - See <a href="https://github.com/teleclimber/Dropserver" target="_blank" rel="noopener noreferrer">original repository</a> for detailsStep 7: Initialize Git and Push to GitHub
# Initialize git repositorygit init
# Add all filesgit add Dockerfile config.json .dockerignore .env.example README.md
# Commit filesgit commit -m "Initial Dropserver deployment configuration"
# Add GitHub remote (replace with your repository URL)git remote add origin https://github.com/yourusername/dropserver-klutch.git
# Push to GitHubgit branch -M maingit push -u origin mainDeploying to Klutch.sh
Step 1: Configure Domain DNS
Before deploying, configure your domainβs DNS:
- Add an A record for your root domain pointing to Klutch.sh IP
- Add a wildcard A record (*) pointing to the same IP
- Repeat for AAAA records if using IPv6
- Wait for DNS propagation (typically 5-15 minutes)
Example DNS Configuration:
Type Name ValueA @ <klutch-ip>A * <klutch-ip>AAAA @ <klutch-ipv6>AAAA * <klutch-ipv6>Step 2: Create New App on Klutch.sh
- Navigate to the Klutch.sh dashboard
- Click **Create New App** or **New Deployment**
- Select **GitHub** as your source
- Authenticate with GitHub if not already connected
- Choose your Dropserver repository from the list
- Select the `main` branch for deployment
Step 3: Configure Application Settings
- **App Name**: Choose a unique name (e.g., `dropserver-platform`)
- **Traffic Type**: Select **HTTP** traffic
- **Internal Port**: Set to `3003` (Dropserver's HTTP port)
Important: Dropserver requires HTTP traffic mode for subdomain routing. The server listens on port 3003 internally.
Step 4: Configure Custom Domain
- In app settings, navigate to **Custom Domains**
- Add your domain (e.g., `example.com`)
- Enable **Wildcard Subdomain** support
- Klutch.sh will automatically provision SSL certificates
Step 5: Attach Persistent Volume
- Navigate to **Volumes** in app settings
- Click **Add Volume** or **Create Volume**
- **Mount Path**: `/srv/dropserver`
- **Size**: Start with 10GB (increase based on usage)
- Click **Save** or **Attach**
Storage Recommendations:
- Small deployment (1-5 users, 5-10 apps): 10GB
- Medium deployment (5-20 users, 10-25 apps): 25GB
- Large deployment (20+ users, 25+ apps): 50GB+
Step 6: Set Environment Variables (Optional)
While configuration is in config.json, you can override settings via environment variables:
DROPSERVER_DOMAIN=yourdomain.comDROPSERVER_SUBDOMAIN=dropidNote: The Dockerfile uses the config.json file by default. Environment variables are useful for testing different configurations.
Step 7: Deploy Application
- Review all settings in the Klutch.sh dashboard
- Click **Deploy** or **Create App**
- Klutch.sh will automatically detect the Dockerfile and build your container
- Wait for the build to complete (typically 5-8 minutes for first deployment)
- Monitor build logs for any errors
- Once deployed, your app will be available at `https://dropid.yourdomain.com`
Step 8: Initial Admin Setup
- After successful deployment, check application logs in Klutch.sh dashboard
- Look for line containing `setup_key_reveal=` followed by a secret URL
- Copy the complete URL (e.g., `https://dropid.yourdomain.com/setup?key=...`)
- Open the URL in your browser to access the admin registration page
- Create your admin account with email and password
- Log in with your new admin credentials
Security Note: The setup key is only generated on first run when no admin exists. Save your admin credentials securely.
Step 9: Create Your Dropid
After logging in as admin:
- Navigate to **Dropids** section in admin interface
- Click **Create New Dropid**
- Enter your display name and optionally upload an avatar
- Save your dropid (this is your user identity for appspaces)
Step 10: Install Your First App
- Navigate to **Apps** section in admin interface
- Choose installation method: - **From URL**: Enter app package URL (e.g., from Leftovers or Shopping List) - **Manual Upload**: Upload a `.tar.gz` app package
- Dropserver validates and installs the app
- Navigate to **Appspaces** to create an instance
Step 11: Create an Appspace
- In **Appspaces** section, click **Create New Appspace**
- Select the app you installed
- Choose a subdomain (e.g., `shopping` becomes `shopping.yourdomain.com`)
- Add yourself (your dropid) as a user
- Optionally add other dropids for collaboration
- Click **Create** to launch the appspace
- Access your appspace at `https://your-subdomain.yourdomain.com`
Understanding Appspaces and Applications
What is an Appspace?
An appspace is a running instance of an application with:
- Unique subdomain: Isolated origin for security
- Private data storage: Each appspace has its own database/files
- User list: Control who can access this specific instance
- Resource isolation: Runs in its own Deno sandbox
- Independent configuration: Settings separate from other appspaces
Think of appspaces like separate installations of the same app. You might have:
shopping.yourdomain.com- Shopping list for your householdgroceries.yourdomain.com- Shopping list for your extended familyleftovers.yourdomain.com- Fridge tracker for your home
All three are instances of the same shopping list app, but with separate data and users.
How Apps Work
Dropserver apps are TypeScript/JavaScript packages with:
App Manifest (app-manifest.json):
{ "name": "Shopping List", "version": "1.2.0", "short-description": "Collaborative shopping lists", "entrypoint": "backend/app.ts", "schema": 2, "permissions": { "files": ["read", "write"], "env": [] }}Backend Code (backend/app.ts):
import { dropserver } from "dropserver-lib";
// Handle HTTP requestsdropserver.handleRoute("/", async (ctx) => { return new Response("Hello from Dropserver!", { headers: { "content-type": "text/html" } });});
// Private route requiring authenticationdropserver.handleRoute("/api/items", async (ctx) => { if (!ctx.user) { return new Response("Unauthorized", { status: 401 }); }
// Access appspace data const items = await ctx.appspace.readFile("items.json"); return new Response(items, { headers: { "content-type": "application/json" } });});Frontend Assets: HTML, CSS, JavaScript served statically
App Permissions
Dropserverβs security model requires explicit permissions:
- Files: Read/write access to appspace data directory
- Network: Outbound HTTP requests (must specify domains)
- Environment: Access to environment variables
- None by default: Apps are sandboxed with zero permissions
Users can review permissions before installing apps.
Getting Started with Dropserver
Managing Users and Dropids
Create Additional Dropids (for other users):
- Admin logs into admin interface
- Navigate to Dropids section
- Click Create New Dropid
- Provide display name and email
- User receives invitation email
- User sets password and accesses their dropid
Invite Users to Appspaces:
- Open appspace settings
- Click Add User
- Select dropid from list
- User gets access automatically
Installing Apps from URLs
Many Dropserver apps can be installed via URL:
Leftovers (fridge management):
https://leftovers.olivierforget.net/app/Shopping List:
https://shoppinglist.olivierforget.net/Installation steps:
- Navigate to Apps in admin interface
- Click Install from URL
- Paste app URL
- Dropserver downloads and validates
- App appears in your apps list
Exporting and Importing Appspaces
Export an Appspace:
- Open appspace settings
- Click Export
- Dropserver generates
.tar.gzarchive - Download contains app code, data, and metadata
- Store securely as backup
Import an Appspace:
- Navigate to Appspaces section
- Click Import
- Upload appspace
.tar.gzfile - Choose subdomain for restored appspace
- Dropserver migrates data if app version differs
This allows moving appspaces between Dropserver instances or restoring from backups.
Managing Appspace Data
View Data:
- Open appspace settings
- Navigate to Files tab
- Browse appspace data directory
- View/download individual files
Backup Data:
- Use appspace export (includes code and data)
- Or manually backup
/srv/dropservervolume - Schedule regular backups via Klutch.sh
Data Migration:
When updating apps to new versions:
- Dropserver detects schema change
- Runs migration scripts defined in app
- Updates appspace to new schema
- Preserves user data through migration
Sample Code Examples
TypeScript - Basic Dropserver App
// backend/app.ts - Simple note-taking appimport { dropserver } from "dropserver-lib";
interface Note { id: string; title: string; content: string; created: string;}
// Home page - public routedropserver.handleRoute("/", async (ctx) => { if (!ctx.user) { return new Response(` <!DOCTYPE html> <html> <head><title>Notes App</title></head> <body> <h1>Notes App</h1> <p>Please <a href="/login">log in</a> to access your notes.</p> </body> </html> `, { headers: { "content-type": "text/html" } }); }
// Serve notes interface for authenticated users return new Response(` <!DOCTYPE html> <html> <head> <title>My Notes</title> <script src="/static/app.js"></script> </head> <body> <h1>Welcome, ${ctx.user.display_name}!</h1> <div id="notes-container"></div> <button onclick="createNote()">New Note</button> </body> </html> `, { headers: { "content-type": "text/html" } });});
// API - Get all notesdropserver.handleRoute("/api/notes", async (ctx) => { if (!ctx.user) { return new Response("Unauthorized", { status: 401 }); }
try { const notesData = await ctx.appspace.readFile("notes.json"); const notes: Note[] = JSON.parse(notesData);
return new Response(JSON.stringify(notes), { headers: { "content-type": "application/json" } }); } catch (error) { // File doesn't exist yet - return empty array return new Response("[]", { headers: { "content-type": "application/json" } }); }});
// API - Create new notedropserver.handleRoute("/api/notes", async (ctx) => { if (!ctx.user) { return new Response("Unauthorized", { status: 401 }); }
if (ctx.request.method !== "POST") { return new Response("Method not allowed", { status: 405 }); }
const body = await ctx.request.json(); const newNote: Note = { id: crypto.randomUUID(), title: body.title || "Untitled", content: body.content || "", created: new Date().toISOString() };
// Load existing notes let notes: Note[] = []; try { const notesData = await ctx.appspace.readFile("notes.json"); notes = JSON.parse(notesData); } catch { // File doesn't exist - start with empty array }
// Add new note and save notes.push(newNote); await ctx.appspace.writeFile("notes.json", JSON.stringify(notes, null, 2));
return new Response(JSON.stringify(newNote), { status: 201, headers: { "content-type": "application/json" } });});JavaScript - App Manifest
{ "name": "Simple Notes", "version": "1.0.0", "short-description": "A minimal note-taking app for Dropserver", "description": "Create and manage personal notes with a clean interface. All notes are stored privately in your appspace.", "icon": "icon.png", "authors": [ { "name": "Your Name", "email": "you@example.com" } ], "code": { "entrypoint": "backend/app.ts", "location": "./" }, "schema": 1, "migrations": [], "permissions": { "files": { "read": true, "write": true }, "routes": { "public": ["/", "/static/*"], "authenticated": ["/api/*"] } }, "release-date": "2025-12-17"}TypeScript - Data Migration Example
// migrations/v2.ts - Migrate from v1 to v2 schemaimport { dropserver } from "dropserver-lib";
interface NoteV1 { id: string; text: string;}
interface NoteV2 { id: string; title: string; content: string; created: string;}
dropserver.handleMigration(async (ctx) => { console.log("Migrating notes from v1 to v2...");
// Read old format const oldNotesData = await ctx.appspace.readFile("notes.json"); const oldNotes: NoteV1[] = JSON.parse(oldNotesData);
// Convert to new format const newNotes: NoteV2[] = oldNotes.map(note => ({ id: note.id, title: note.text.slice(0, 50), // First 50 chars as title content: note.text, created: "2025-01-01T00:00:00Z" // Default date for old notes }));
// Save new format await ctx.appspace.writeFile("notes.json", JSON.stringify(newNotes, null, 2));
console.log(`Migrated ${newNotes.length} notes to v2 schema`);});JavaScript - Frontend Integration
// static/app.js - Frontend code for notes appasync function loadNotes() { try { const response = await fetch('/api/notes'); const notes = await response.json();
const container = document.getElementById('notes-container'); container.innerHTML = '';
notes.forEach(note => { const noteEl = document.createElement('div'); noteEl.className = 'note'; noteEl.innerHTML = ` <h3>${escapeHtml(note.title)}</h3> <p>${escapeHtml(note.content)}</p> <small>Created: ${new Date(note.created).toLocaleDateString()}</small> `; container.appendChild(noteEl); }); } catch (error) { console.error('Failed to load notes:', error); }}
async function createNote() { const title = prompt('Note title:'); const content = prompt('Note content:');
if (!title || !content) return;
try { const response = await fetch('/api/notes', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ title, content }) });
if (response.ok) { loadNotes(); // Reload list } else { alert('Failed to create note'); } } catch (error) { console.error('Failed to create note:', error); }}
function escapeHtml(text) { const div = document.createElement('div'); div.textContent = text; return div.innerHTML;}
// Load notes on page loaddocument.addEventListener('DOMContentLoaded', loadNotes);Go - Custom ds-host Configuration
// config-generator.go - Generate Dropserver config programmaticallypackage main
import ( "encoding/json" "fmt" "os")
type Config struct { DataDir string `json:"data-dir"` Server ServerConfig `json:"server"` ExternalAccess AccessConfig `json:"external-access"` Sandbox SandboxConfig `json:"sandbox"`}
type ServerConfig struct { HTTPPort int `json:"http-port"` NoTLS bool `json:"no-tls"`}
type AccessConfig struct { Scheme string `json:"scheme"` Domain string `json:"domain"` Subdomain string `json:"subdomain"`}
type SandboxConfig struct { SocketsDir string `json:"sockets-dir"` UseBubblewrap bool `json:"use-bubblewrap"` UseCgroups bool `json:"use-cgroups"` MemoryHighMB int `json:"memory-high-mb"`}
func main() { domain := os.Getenv("DROPSERVER_DOMAIN") if domain == "" { domain = "example.com" }
config := Config{ DataDir: "/srv/dropserver", Server: ServerConfig{ HTTPPort: 3003, NoTLS: true, }, ExternalAccess: AccessConfig{ Scheme: "https", Domain: domain, Subdomain: "dropid", }, Sandbox: SandboxConfig{ SocketsDir: "/var/run/dropserver", UseBubblewrap: false, UseCgroups: false, MemoryHighMB: 512, }, }
jsonData, err := json.MarshalIndent(config, "", " ") if err != nil { fmt.Fprintf(os.Stderr, "Error generating config: %v\n", err) os.Exit(1) }
err = os.WriteFile("/etc/dropserver/config.json", jsonData, 0644) if err != nil { fmt.Fprintf(os.Stderr, "Error writing config: %v\n", err) os.Exit(1) }
fmt.Println("Config generated successfully")}Bash - Deployment Helper Script
#!/bin/bash# deploy-dropserver.sh - Helper script for Dropserver deployment
set -e
DOMAIN="${1:-example.com}"SUBDOMAIN="${2:-dropid}"DATA_DIR="/srv/dropserver"SOCKETS_DIR="/var/run/dropserver"
echo "π Dropserver Deployment Helper"echo "================================"echo "Domain: $DOMAIN"echo "Admin subdomain: $SUBDOMAIN.$DOMAIN"echo ""
# Create directoriesecho "π Creating directories..."mkdir -p "$DATA_DIR" "$SOCKETS_DIR"chmod 755 "$DATA_DIR" "$SOCKETS_DIR"
# Generate configecho "βοΈ Generating configuration..."cat > config.json <<EOF{ "data-dir": "$DATA_DIR", "server": { "http-port": 3003, "no-tls": true }, "external-access": { "scheme": "https", "domain": "$DOMAIN", "subdomain": "$SUBDOMAIN" }, "sandbox": { "sockets-dir": "$SOCKETS_DIR", "use-bubblewrap": false, "use-cgroups": false, "memory-high-mb": 512 }}EOF
echo "β
Configuration generated: config.json"echo ""
# Check if Deno is installedif ! command -v deno &> /dev/null; then echo "β οΈ Warning: Deno not found. Installing..." curl -fsSL https://deno.land/install.sh | sh export DENO_INSTALL="$HOME/.deno" export PATH="$DENO_INSTALL/bin:$PATH" echo "β
Deno installed"else echo "β
Deno found: $(deno --version | head -1)"fi
echo ""echo "π Setup complete!"echo ""echo "Next steps:"echo "1. Update config.json with your actual domain"echo "2. Build Docker image: docker build -t dropserver ."echo "3. Push to GitHub and deploy on Klutch.sh"echo "4. Configure DNS (A and * wildcard records)"echo "5. Attach persistent volume at $DATA_DIR"echo "6. Access admin at https://$SUBDOMAIN.$DOMAIN"Advanced Configuration
Custom Subdomain Management
By default, Dropserver uses dropid as the admin subdomain. You can customize this:
In config.json:
{ "external-access": { "subdomain": "admin", "domain": "yourdomain.com" }}Access admin at: https://admin.yourdomain.com
Appspace subdomains remain separate (e.g., shopping.yourdomain.com).
Memory and Resource Limits
Control sandbox resource usage:
{ "sandbox": { "memory-high-mb": 1024, "use-cgroups": false }}Recommendations:
- Light usage (1-5 active appspaces): 512MB
- Medium usage (5-15 active appspaces): 1024MB
- Heavy usage (15+ active appspaces): 2048MB+
Note: use-cgroups requires systemd delegation, unavailable in standard Docker containers.
TLS Configuration Options
Klutch.sh handles TLS, but for reference, here are Dropserverβs TLS modes:
Behind Reverse Proxy (Klutch.sh default):
{ "server": { "http-port": 3003, "no-tls": true }}Direct TLS (if self-hosting):
{ "server": { "tls-port": 443, "http-port": 80 }, "manage-certificates": { "enable": true, "acme-account-email": "you@example.com" }}Custom Certificates (if self-hosting):
{ "server": { "tls-port": 443, "ssl-cert": "/path/to/cert.crt", "ssl-key": "/path/to/key.key" }}App Permission Management
Review and manage app permissions:
- Navigate to Apps in admin interface
- Select an app to view details
- Review Permissions tab
- See exactly what the app can access:
- File read/write capabilities
- Network access destinations
- Environment variable access
Permission Types:
- Files:
read,write, or both - Network: List of allowed domains
- Environment: Specific env variables
Apps cannot access anything not explicitly declared.
Monitoring and Logging
Monitor your Dropserver instance:
View Logs in Klutch.sh:
- Navigate to app in Klutch.sh dashboard
- Click Logs tab
- View real-time application output
- Look for:
- Appspace creation/deletion
- User authentication events
- App installation success/failure
- Sandbox initialization
- Error messages
Log Levels:
Dropserver logs various events:
- INFO: Normal operations (appspace created, user logged in)
- WARN: Non-critical issues (slow sandbox startup)
- ERROR: Failures requiring attention (app install failed)
Metrics to Monitor:
- Number of active appspaces
- Memory usage per sandbox
- Request latency
- Failed authentication attempts
- Disk usage in
/srv/dropserver
Production Best Practices
Security Hardening
Domain and Subdomain Security:
- Always use HTTPS (Klutch.sh provides automatically)
- Configure wildcard SSL for all subdomains
- Avoid predictable subdomain names for sensitive appspaces
- Regularly rotate admin passwords
App Vetting:
- Review app permissions before installation
- Only install apps from trusted sources
- Inspect app code if self-hosting sensitive data
- Keep apps updated to latest versions
User Management:
- Use strong passwords for all accounts
- Limit dropid creation to trusted administrators
- Regularly audit appspace access lists
- Remove unused dropids and appspaces
Backup Strategy:
- Schedule regular volume backups via Klutch.sh
- Export critical appspaces manually
- Store backups in separate location
- Test restore procedures periodically
Performance Optimization
Resource Allocation:
- Start with conservative memory limits
- Monitor actual usage via logs
- Scale up if seeing sandbox initialization delays
- Consider separating high-traffic appspaces to dedicated instances
Storage Management:
- Regularly audit
/srv/dropserverdisk usage - Archive or delete unused appspaces
- Export and remove old appspace versions
- Plan for growth (appspace data accumulates over time)
App Selection:
- Prefer lightweight apps for personal use
- Avoid resource-intensive apps unless necessary
- Monitor per-app resource consumption
- Consider app alternatives if performance suffers
Scaling Strategies
Vertical Scaling (same instance, more resources):
- Increase Dropserver container memory/CPU via Klutch.sh
- Expand persistent volume size
- Adjust
memory-high-mbin configuration - Monitor for resource contention
Horizontal Scaling (multiple instances):
- Deploy separate Dropserver instances for different purposes
- One for family, one for work, one for clubs
- Each instance has independent data and users
- Use subdomain-per-instance routing (family.yourdomain.com, work.yourdomain.com)
Data Management:
- Export/import appspaces between instances
- Migrate high-usage appspaces to dedicated Dropserver
- Archive inactive appspaces to free resources
- Plan storage growth based on app types
Backup and Disaster Recovery
Automated Backups:
- Configure Klutch.sh volume snapshots (if available)
- Or use scheduled export scripts
- Store backups in cloud storage (S3, Backblaze, etc.)
- Retain multiple backup generations (daily, weekly, monthly)
Manual Export Procedure:
# Export all appspaces (theoretical script)for appspace in $(list-appspaces); do export-appspace $appspace /backups/$appspace-$(date +%Y%m%d).tar.gzdoneRestore Procedure:
- Deploy fresh Dropserver instance on Klutch.sh
- Complete initial admin setup
- Install required apps (from URLs or uploads)
- Import appspace archives one by one
- Verify data integrity and user access
Disaster Recovery Checklist:
- Admin credentials backed up securely
- Domain DNS records documented
- App URLs or packages saved externally
- Appspace exports stored off-instance
- Restore procedure tested at least once
Troubleshooting
Issue: Admin Setup Key Not Found
Symptoms: Canβt find setup_key_reveal URL in logs
Solutions:
-
Check logs carefully:
- Look for line starting with
setup_key_reveal= - Check Klutch.sh dashboard Logs tab
- Search for βsetupβ in log output
- Look for line starting with
-
Already have admin:
- Setup key only appears on first run
- If admin already exists, log in normally
- Navigate to admin subdomain (e.g., dropid.yourdomain.com)
-
Reset database:
- If needed, delete volume data and redeploy
- Dropserver will regenerate setup key
- WARNING: This deletes all data
Issue: Subdomain Not Accessible
Symptoms: Canβt access admin or appspace subdomains
Solutions:
-
Check DNS configuration:
Terminal window # Verify A recorddig yourdomain.com# Verify wildcarddig random-subdomain.yourdomain.comBoth should point to Klutch.sh IP
-
DNS propagation:
- Allow 5-15 minutes after DNS changes
- Use WhatsMyDNS to check global propagation
- Try accessing from different networks
-
Klutch.sh domain configuration:
- Verify custom domain added in dashboard
- Ensure wildcard subdomain support enabled
- Check SSL certificate provisioning status
-
Configuration mismatch:
- Verify
external-access.domainin config.json - Ensure matches actual domain
- Redeploy after config changes
- Verify
Issue: App Installation Fails
Symptoms: Error when installing app from URL or upload
Solutions:
-
Check app URL:
- Ensure URL is accessible from Dropserver
- Try accessing URL in browser
- Verify URL points to valid app package
-
Manifest validation:
- App manifest may have errors
- Check Dropserver logs for validation errors
- Contact app author if official app
-
Network permissions:
- Dropserver needs outbound network access
- Verify no firewall blocking Dropserver
- Try manual upload instead of URL
-
Deno not found:
- Ensure Deno installed in container
- Check Dockerfile includes Deno installation
- Verify
denoin PATH
Issue: Appspace Wonβt Start
Symptoms: Appspace subdomain returns error or timeout
Solutions:
-
Check logs:
- Look for sandbox initialization errors
- Check for Deno runtime errors
- Verify app code compatibility
-
Memory limits:
- Increase
memory-high-mbin config - Check if multiple appspaces competing for resources
- Scale up Dropserver container resources
- Increase
-
Deno version compatibility:
- Some apps require specific Deno versions
- Update Deno in Dockerfile if needed
- Check app documentation for requirements
-
Data corruption:
- Try exporting and reimporting appspace
- Check appspace data files for corruption
- Restore from backup if available
Issue: Slow Appspace Performance
Symptoms: Appspaces take long to respond or load
Solutions:
-
Resource constraints:
- Monitor container CPU/memory usage
- Increase Klutch.sh resource allocation
- Reduce number of active appspaces
-
Cold start:
- First request to idle appspace takes longer
- Sandbox needs to initialize
- Subsequent requests should be faster
-
Storage I/O:
- Check volume IOPS performance
- Large appspace data files may be slow
- Consider SSD-backed volumes
-
Network latency:
- Verify Klutch.sh region proximity
- Check DNS resolution time
- Test from different networks
Issue: Lost Admin Access
Symptoms: Forgot admin password, canβt log in
Solutions:
-
Reset not available:
- Dropserver doesnβt have password reset UI yet
- Need database-level intervention
-
Backup and restore:
- If you have backups, restore to known state
- Create new admin during setup
-
Prevention:
- Store admin credentials in password manager
- Document recovery procedures
- Keep multiple admin accounts if critical
Debugging Tools
Check Dropserver Version:
ds-host -versionVerify Deno Installation:
deno --versionInspect Configuration:
cat /etc/dropserver/config.jsonTest Subdomain Resolution:
curl -I https://dropid.yourdomain.comView Appspace Data:
ls -la /srv/dropserver/appspaces/Building Custom Dropserver Apps
App Development Workflow
-
Set up ds-dev locally:
- Download ds-dev from releases
- Configure for local development
- Create new app directory
-
Create app manifest:
- Define name, version, permissions
- Specify entrypoint file
- Set schema version
-
Write backend code:
- Use TypeScript for type safety
- Implement route handlers
- Access Dropserver APIs
-
Add frontend assets:
- HTML, CSS, JavaScript files
- Serve via static routes
- Call backend APIs
-
Test in ds-dev:
- Run app locally
- Test all routes and features
- Debug with console logs
-
Package app:
- Create
.tar.gzarchive - Upload to your Dropserver
- Or host URL for distribution
- Create
App Template Structure
my-app/βββ app-manifest.json # App metadata and configβββ backend/β βββ app.ts # Main backend entrypointβ βββ lib/ # Shared backend codeβββ frontend/β βββ index.html # Main UIβ βββ app.js # Frontend logicβ βββ styles.css # Stylingβββ migrations/ # Schema migration scriptsβ βββ v2.tsβββ icon.png # App icon (square, 256x256+)Dropserver API Reference
Context Object (ctx):
interface AppContext { request: Request; // HTTP request user: User | null; // Authenticated user or null appspace: { readFile(path: string): Promise<string>; writeFile(path: string, data: string): Promise<void>; deleteFile(path: string): Promise<void>; listFiles(dir: string): Promise<string[]>; }; settings: Record<string, any>; // Appspace settings}Route Handlers:
dropserver.handleRoute(pattern: string, handler: (ctx) => Response)Migration Handlers:
dropserver.handleMigration(handler: (ctx) => Promise<void>)Publishing Your App
Option 1: Self-host app package:
- Build
.tar.gzarchive - Host on your web server
- Share URL with users
- Users install via URL in their Dropserver
Option 2: GitHub releases:
- Create GitHub repository for your app
- Tag releases with version numbers
- Attach
.tar.gzto release - Share GitHub release URL
Option 3: Dropserver app directory (future):
- Official app directory planned
- Submit apps for review
- Users discover and install via UI
Additional Resources
- Dropserver Official Website
- Dropserver Documentation
- Dropserver GitHub Repository
- App Building Tutorial
- ds-dev Development Tool
- Security Model Documentation
- Application Model Overview
- Deno Runtime
- Leftovers App (example Dropserver app)
- Shopping List App (example Dropserver app)
- Developer Blog (Olivier Forget, creator)
Related Guides
- Need a database for your apps? See our PostgreSQL guide
- Want to add caching? Check our Redis guide
- Building TypeScript apps? See our Node.js guides
- Looking for app ideas? See our open-source software guides
You now have a fully functional Dropserver platform deployed on Klutch.sh! Users can install personal web applications, create isolated appspaces with subdomain routing, invite collaborators, and enjoy the freedom of self-hosted services. Build custom apps with TypeScript, manage everything through the web interface, and maintain complete control over your data and privacy.