Deploying a Cockpit CMS App
Cockpit CMS is a lightweight, API-first headless content management system designed to simplify content creation and distribution across multiple channels. Unlike traditional CMS platforms that couple content with presentation, Cockpit focuses purely on content management and delivery, providing a flexible JSON API that can be consumed by any frontend framework, mobile app, or static site generator. Built with PHP and supporting both SQLite and MongoDB, Cockpit offers a clean, intuitive interface for defining content models (Collections), managing single-page content (Singletons), handling forms, and organizing assets—all without imposing a specific technology stack on your frontend. Whether you’re building a portfolio site, a corporate blog, a mobile application backend, or a complex digital experience, Cockpit provides the flexibility and performance needed for modern content delivery.
This comprehensive guide walks through deploying Cockpit CMS to Klutch.sh using a Dockerfile for reliable, containerized deployment. You’ll learn how to set up Cockpit with persistent storage for uploads and data, create a production-ready Dockerfile with Nginx and PHP-FPM, configure API access, manage content models, implement security best practices, configure custom domains, set up monitoring and logging, and troubleshoot common issues. By the end of this guide, you’ll have a production-ready Cockpit CMS instance running on Klutch.sh’s global infrastructure with automatic HTTPS, optimized performance, and reliable hosting for your headless content needs.
Prerequisites
- Docker installed locally for testing (optional but recommended)
- Git installed locally and a GitHub account (Klutch.sh uses GitHub as the only git source)
- Klutch.sh account with access to the dashboard at klutch.sh/app
- Text editor or IDE for code editing (VS Code recommended)
- Basic PHP knowledge (helpful but not required)
- Understanding of Headless CMS concepts (API-first content delivery)
Understanding Cockpit CMS Architecture
Technology Stack
Backend:
- PHP 8.1+ (Modern PHP codebase)
- SQLite (default) or MongoDB database
- Built-in REST API
- Event-driven architecture
- Redis support for caching
Frontend (Admin UI):
- Vue.js / Riot.js based interface
- Responsive design
- Drag-and-drop asset management
- JSON-based configuration
- Extensible via addons
Key Features
Content Management:
- Collections: Define repeatable content structures (e.g., blog posts, products)
- Singletons: Manage unique content pages (e.g., settings, homepage)
- Forms: Handle form submissions and validation
- Assets: Built-in file manager with image processing
API-First:
- RESTful API for all content
- Token-based authentication
- Filtering and sorting capabilities
- Webhooks for external integrations
- GraphQL support (via addon)
Flexibility:
- Schema-less data structure
- Custom field types
- Multi-language support (Localization)
- Revisions and versioning
- Role-based access control (ACL)
Project Setup
1. Create Project Directory
Create a project directory for Cockpit deployment:
mkdir cockpit-deploymentcd cockpit-deploymentgit init2. Create Directory Structure
Create necessary directories:
mkdir -p {config,storage,scripts}Resulting project structure:
cockpit-deployment/├── Dockerfile├── entrypoint.sh├── nginx.conf├── config/│ └── config.php├── storage/│ └── (persistent data)├── .dockerignore├── .gitignore└── README.md3. Create .gitignore File
Create .gitignore to exclude unnecessary files:
.env.env.local*.log.DS_Store.vscode.ideastorage/data/storage/cache/storage/tmp/storage/uploads/vendor/node_modules/4. Create .dockerignore File
Create .dockerignore to exclude files from Docker build:
.git.gitignore*.log.envstorage/.DS_Store.vscode.ideatests/README.mdCreating a Production Dockerfile
Create a Dockerfile for production deployment:
# === Build Stage ===FROM composer:2.6 AS builder
WORKDIR /tmp
# Download Cockpit CMS# Using the latest stable release from the official repositoryRUN git clone https://github.com/Cockpit-HQ/Cockpit.git . && \ git checkout master && \ composer install --no-dev --optimize-autoloader
# === Production Stage ===FROM php:8.2-fpm-alpine
WORKDIR /var/www/html
# Install system dependenciesRUN apk add --no-cache \ nginx \ curl \ libpng \ libjpeg-turbo \ libwebp \ freetype \ zip \ unzip \ sqlite \ icu-libs \ oniguruma \ libzip \ imagemagick \ && apk add --no-cache --virtual .build-deps \ libpng-dev \ libjpeg-turbo-dev \ libwebp-dev \ freetype-dev \ sqlite-dev \ icu-dev \ oniguruma-dev \ libzip-dev \ imagemagick-dev \ # Install PHP extensions && docker-php-ext-configure gd --with-freetype --with-jpeg --with-webp \ && docker-php-ext-install -j$(nproc) \ gd \ pdo \ pdo_sqlite \ opcache \ intl \ mbstring \ zip \ exif \ # Install MongoDB extension (optional, but good for scalability) && pecl install mongodb \ && docker-php-ext-enable mongodb \ # Cleanup && apk del .build-deps \ && rm -rf /tmp/*
# Configure PHPRUN echo "memory_limit = 256M" > /usr/local/etc/php/conf.d/memory-limit.ini && \ echo "upload_max_filesize = 64M" > /usr/local/etc/php/conf.d/uploads.ini && \ echo "post_max_size = 64M" >> /usr/local/etc/php/conf.d/uploads.ini && \ echo "expose_php = Off" > /usr/local/etc/php/conf.d/security.ini
# Configure PHP-FPMRUN echo "[www]" > /usr/local/etc/php-fpm.d/zz-docker.conf && \ echo "listen = 127.0.0.1:9000" >> /usr/local/etc/php-fpm.d/zz-docker.conf && \ echo "pm = dynamic" >> /usr/local/etc/php-fpm.d/zz-docker.conf && \ echo "pm.max_children = 10" >> /usr/local/etc/php-fpm.d/zz-docker.conf && \ echo "pm.start_servers = 2" >> /usr/local/etc/php-fpm.d/zz-docker.conf && \ echo "pm.min_spare_servers = 1" >> /usr/local/etc/php-fpm.d/zz-docker.conf && \ echo "pm.max_spare_servers = 3" >> /usr/local/etc/php-fpm.d/zz-docker.conf
# Copy Nginx configurationCOPY nginx.conf /etc/nginx/nginx.conf
# Copy Cockpit source from builderCOPY --from=builder --chown=www-data:www-data /tmp /var/www/html
# Create storage directories and set permissionsRUN mkdir -p /var/www/html/storage/data \ /var/www/html/storage/cache \ /var/www/html/storage/tmp \ /var/www/html/storage/uploads \ && chown -R www-data:www-data /var/www/html/storage \ && chmod -R 775 /var/www/html/storage
# Copy entrypoint scriptCOPY entrypoint.sh /entrypoint.shRUN chmod +x /entrypoint.sh
# Health checkHEALTHCHECK --interval=30s --timeout=10s --start-period=10s --retries=3 \ CMD curl -f http://localhost:8080/ || exit 1
# Expose portEXPOSE 8080
# Start servicesCMD ["/entrypoint.sh"]Dockerfile Explanation
- Build Stage: Uses Composer to fetch and install Cockpit CMS dependencies.
- Production Stage: Uses Alpine Linux with PHP 8.2 FPM for a lightweight image.
- Dependencies: Installs GD (image processing), SQLite (database), MongoDB (optional DB), and other required extensions.
- Configuration: Sets PHP memory limits and upload sizes suitable for a CMS.
- Permissions: Ensures the
storagedirectory is writable by the web server user. - Web Server: Includes Nginx to serve the application and proxy to PHP-FPM.
Create Nginx Configuration
Create nginx.conf to handle web traffic:
user www-data;worker_processes auto;pid /run/nginx.pid;
events { worker_connections 1024; use epoll;}
http { include /etc/nginx/mime.types; default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" ' '$status $body_bytes_sent "$http_referer" ' '"$http_user_agent" "$http_x_forwarded_for"';
access_log /dev/stdout main; error_log /dev/stderr warn;
sendfile on; tcp_nopush on; tcp_nodelay on; keepalive_timeout 65; types_hash_max_size 2048; client_max_body_size 64M;
server { listen 8080 default_server; server_name _; root /var/www/html; index index.php;
# Security headers add_header X-Frame-Options "SAMEORIGIN"; add_header X-XSS-Protection "1; mode=block"; add_header X-Content-Type-Options "nosniff";
location / { try_files $uri $uri/ /index.php?$query_string; }
# Deny access to sensitive files and directories location ~ ^/(storage|config|lib|modules|vendor) { deny all; return 404; }
location ~ \.php$ { try_files $uri =404; fastcgi_split_path_info ^(.+\.php)(/.+)$; fastcgi_pass 127.0.0.1:9000; fastcgi_index index.php; include fastcgi_params; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; fastcgi_param PATH_INFO $fastcgi_path_info; }
location ~* \.(jpg|jpeg|png|gif|ico|css|js|svg|woff|woff2|ttf|eot)$ { expires 30d; add_header Cache-Control "public, no-transform"; }
location ~ /\. { deny all; access_log off; log_not_found off; } }}Create Entrypoint Script
Create entrypoint.sh to initialize the environment:
#!/bin/sh
set -e
echo "Starting Cockpit CMS initialization..."
# Ensure storage directories exist and have correct permissions# This is crucial when mounting persistent volumesmkdir -p /var/www/html/storage/data \ /var/www/html/storage/cache \ /var/www/html/storage/tmp \ /var/www/html/storage/uploads
# Fix permissions for storage directorychown -R www-data:www-data /var/www/html/storagechmod -R 775 /var/www/html/storage
# Check if installation is freshif [ ! -f "/var/www/html/storage/data/cockpit.sqlite" ] && [ -z "$COCKPIT_MONGODB_URI" ]; then echo "Initializing new SQLite database..." # Cockpit auto-initializes on first access, but we ensure the file can be created touch /var/www/html/storage/data/cockpit.sqlite chown www-data:www-data /var/www/html/storage/data/cockpit.sqlitefi
# Start PHP-FPM and Nginxecho "Starting PHP-FPM and Nginx..."php-fpm -Dnginx -g "daemon off;"Deploying with Docker on Klutch.sh
Klutch.sh automatically detects a Dockerfile in your repository root and uses it for deployment.
Prerequisites for Docker Deployment
- Your Cockpit CMS project pushed to GitHub with Dockerfile
- Persistent storage configured for the
storagedirectory - Environment variables for security and configuration
Steps to Deploy with Docker
-
Prepare Your Repository
Commit and push all necessary files:
Terminal window git add Dockerfile entrypoint.sh nginx.conf .dockerignoregit commit -m "Add Docker deployment configuration for Cockpit CMS"git push origin main -
Log In to Klutch.sh Dashboard
Go to klutch.sh/app and sign in with your GitHub account.
-
Create a Project
Navigate to the Projects section and create a new project for your Cockpit CMS instance.
-
Create an App
Click “Create App” and select your GitHub repository containing the Cockpit Docker configuration.
-
Select the Branch
Choose the branch you want to deploy (typically
main). -
Configure Traffic Type
Select HTTP as the traffic type.
-
Set the Internal Port
Set the internal port to
8080– this is the port configured in Nginx. -
Add Environment Variables
Configure environment variables for your Cockpit instance:
# Application SettingsAPP_NAME=CockpitCMSAPP_ENV=productionCOCKPIT_SESSION_NAME=cockpit_sessionCOCKPIT_SALT=generate_a_long_random_string_here# Optional: MongoDB Configuration (if not using SQLite)# COCKPIT_MONGODB_URI=mongodb://user:pass@host:27017/cockpit# PHP SettingsPHP_MEMORY_LIMIT=256MPHP_UPLOAD_MAX_FILESIZE=64MNote:
COCKPIT_SALTis critical for security. Generate a strong random string. -
Configure Persistent Storage
Cockpit stores all data (SQLite database, uploads, cache, accounts) in the
storagedirectory. You must persist this directory to prevent data loss on redeployment.- After creating the app, go to Volumes
- Add a volume:
- Mount path:
/var/www/html/storage - Size: 2-5 GiB (adjust based on expected media uploads)
- Mount path:
-
Configure Compute Resources
Select appropriate resources:
- Minimum: 0.5 CPU, 512 MB RAM (small sites)
- Recommended: 1 CPU, 1 GB RAM (production sites with moderate traffic)
- Large: 2+ CPU, 2+ GB RAM (high traffic or large asset processing)
-
Deploy
Click “Create” to start deployment. Klutch.sh will:
- Build the Docker image
- Start the Cockpit container
- Assign a public URL (e.g.,
example-app.klutch.sh) - Configure HTTPS automatically
-
Initial Setup
Once deployed, access your app URL (e.g.,
https://example-app.klutch.sh/install).- Follow the on-screen instructions to create the initial admin account.
- Important: If the install page is not visible, try accessing
/auth/logindirectly. The default credentials for a fresh install might beadmin/admin(older versions) or you will be prompted to create one. Immediately change the password.
Cockpit CMS Features and Configuration
1. Content Modeling (Collections)
Collections are the core of Cockpit. They define the structure of your content.
Creating a Collection:
- Navigate to Collections in the dashboard.
- Click Add Collection.
- Define the name (e.g., “Blog Posts”).
- Add fields (Text, Image, Markdown, Boolean, etc.).
- Configure permissions (who can read/write).
Example Fields:
- Title: Text
- Slug: Text (unique)
- Content: Markdown or WYSIWYG
- Featured Image: Asset
- Published: Boolean
2. Singletons
Singletons are for unique content structures, like a “Settings” page or “Homepage” content.
Creating a Singleton:
- Navigate to Singletons.
- Click Add Singleton.
- Define fields similar to Collections.
- Use the API to fetch this single object.
3. Forms
Cockpit can handle form submissions from your frontend.
Creating a Form:
- Navigate to Forms.
- Create a new form (e.g., “Contact”).
- Configure email notifications to receive alerts on submission.
- Post data to
/api/forms/submit/Contactfrom your frontend.
4. Assets Management
The built-in asset manager handles file uploads.
Features:
- Drag and drop uploads
- Image resizing and optimization
- Folder organization
- Public URL generation
5. API Access
Cockpit is API-first. You need API tokens to access content.
Configuring API:
- Go to Settings > API Access.
- Master API Key: Full access (keep secret).
- Custom Keys: Create keys with specific rules (e.g., read-only access to “Blog Posts”).
- Use the token in your requests:
?token=YOUR_API_KEYor headerCockpit-Token: YOUR_API_KEY.
Example Request:
curl "https://example-app.klutch.sh/api/collections/get/posts?token=YOUR_TOKEN"6. Advanced Configuration (config.php)
Cockpit is highly configurable via the config/config.php file. You can mount a custom configuration file or use environment variables to override settings.
Common Configuration Options:
<?phpreturn [ // App name 'app.name' => 'My Project',
// Session name 'session.name' => 'mysession',
// Security salt 'sec-key' => 'your-secret-key',
// I18n 'i18n' => 'en',
// Database configuration (MongoDB example) // 'database' => [ // 'server' => 'mongodb://localhost:27017', // 'options' => ['db' => 'cockpitdb'] // ],
// Mailer configuration 'mailer' => [ 'from' => 'noreply@example.com', 'transport' => 'smtp', 'host' => 'smtp.example.com', 'user' => 'user', 'password' => 'pass', 'port' => 587, 'auth' => true, 'encryption' => 'starttls' ],
// CORS settings 'cors' => [ 'allowedHeaders' => 'X-Requested-With, Content-Type, Origin, Cache-Control, Pragma, Authorization, Accept, Accept-Encoding, Cockpit-Token', 'allowedMethods' => 'PUT, POST, GET, OPTIONS, DELETE', 'allowedOrigins' => '*', 'maxAge' => 1000 ],];7. Webhooks
Webhooks allow Cockpit to notify external systems when content changes.
Setting up Webhooks:
- Go to Settings > Webhooks.
- Click Add Webhook.
- Url: The endpoint to notify (e.g., Netlify build hook).
- Events: Select events to trigger the hook (e.g.,
collections.save.post). - Headers: Add custom headers if needed.
Use Cases:
- Triggering a static site build (Gatsby, Next.js, Hugo) when content is published.
- Sending notifications to Slack/Discord.
- Syncing data with external services.
8. Custom Addons
Cockpit is extensible via addons. You can create custom endpoints, field types, or admin UI extensions.
Installing Addons:
- Download the addon to your local machine.
- Add it to the
modules/addonsdirectory in your repository. - Rebuild your Docker image or mount the addon volume.
Example Addon Structure:
/modules/addons/MyAddon/├── admin.php├── bootstrap.php└── Controller/ └── Admin.phpPersistent Storage
Cockpit relies heavily on the filesystem for storage when using SQLite.
Critical Directory: /var/www/html/storage
This directory contains:
data/: SQLite database files (cockpit.sqlite) and JSON data.uploads/: User-uploaded files and images.cache/: System cache files.tmp/: Temporary files.thumbs/: Generated image thumbnails.
Warning: If you do not mount a persistent volume to /var/www/html/storage, you will lose all your content, accounts, and uploads every time the application restarts or redeploys.
Backup Strategy
To backup your Cockpit instance:
- SQLite: Simply download the
cockpit.sqlitefile from thestorage/datadirectory. - Uploads: Backup the
storage/uploadsdirectory. - MongoDB: If using MongoDB, use
mongodumpto backup the external database.
Security Best Practices
1. Secure the Install
- Change Default Credentials: Immediately change the
adminpassword after installation. - Disable Registration: Ensure public registration is disabled in settings if not needed.
2. API Security
- Least Privilege: Create specific API keys for your frontend. Do not use the Master API key in client-side code.
- Read-Only Keys: For public websites, create keys that only have “Collection: Read” permissions.
3. File Permissions
The Dockerfile sets permissions to 775 for the storage directory. Ensure sensitive configuration files in config/ are not publicly accessible (handled by Nginx configuration).
4. HTTPS
Klutch.sh provides automatic HTTPS. Ensure your frontend forces HTTPS when communicating with the API.
Performance Optimization
1. Caching
Cockpit has built-in caching.
- Enable Redis in
config/config.phpif you have a Redis instance. - Use the
Simplecache (filesystem) by default, which works well with the persistent volume.
2. Image Optimization
Cockpit can resize images on the fly.
- Use the
thumbnailAPI to request specific image sizes for your frontend to reduce bandwidth. - The Dockerfile includes
imagemagickandlibwebpfor efficient image processing.
3. PHP OpCache
The Docker image includes PHP OpCache enabled by default for better performance.
Troubleshooting
Issue 1: “500 Internal Server Error” on Install
Cause: Permissions issue or missing PHP extensions. Solution:
- Check logs in Klutch.sh dashboard.
- Verify
/var/www/html/storageis writable. - Ensure
sqliteextension is installed (included in Dockerfile).
Issue 2: Data Disappears After Restart
Cause: Persistent volume not configured. Solution:
- Go to Volumes in Klutch.sh.
- Ensure a volume is mounted to
/var/www/html/storage.
Issue 3: “404 Not Found” on API Routes
Cause: Nginx configuration issue (URL rewriting). Solution:
- Ensure the
try_files $uri $uri/ /index.php?$query_string;directive is present innginx.conf. - Verify the
location /block is correctly configured.
Issue 4: Uploads Fail
Cause: File size limit exceeded. Solution:
- Check
PHP_UPLOAD_MAX_FILESIZEenv var. - Check
client_max_body_sizeinnginx.conf. - Ensure persistent volume has free space.
External Resources
- Official Cockpit CMS Website
- Cockpit Documentation
- Cockpit GitHub Repository
- Cockpit Community Forum
- Klutch.sh Dashboard
Deploying Cockpit CMS to Klutch.sh gives you a powerful, flexible, and lightweight headless CMS without the overhead of managing servers. By following this guide, you’ve set up a secure, containerized environment with persistent storage, Nginx web server, and PHP-FPM optimization. You can now build your frontend using your favorite framework (React, Vue, Svelte, etc.) and consume your content via Cockpit’s robust API, all hosted reliably on Klutch.sh.