Skip to content

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:

Terminal window
mkdir cockpit-deployment
cd cockpit-deployment
git init

2. Create Directory Structure

Create necessary directories:

Terminal window
mkdir -p {config,storage,scripts}

Resulting project structure:

cockpit-deployment/
├── Dockerfile
├── entrypoint.sh
├── nginx.conf
├── config/
│ └── config.php
├── storage/
│ └── (persistent data)
├── .dockerignore
├── .gitignore
└── README.md

3. Create .gitignore File

Create .gitignore to exclude unnecessary files:

.env
.env.local
*.log
.DS_Store
.vscode
.idea
storage/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
.env
storage/
.DS_Store
.vscode
.idea
tests/
README.md

Creating 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 repository
RUN 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 dependencies
RUN 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 PHP
RUN 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-FPM
RUN 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 configuration
COPY nginx.conf /etc/nginx/nginx.conf
# Copy Cockpit source from builder
COPY --from=builder --chown=www-data:www-data /tmp /var/www/html
# Create storage directories and set permissions
RUN 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 script
COPY entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh
# Health check
HEALTHCHECK --interval=30s --timeout=10s --start-period=10s --retries=3 \
CMD curl -f http://localhost:8080/ || exit 1
# Expose port
EXPOSE 8080
# Start services
CMD ["/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 storage directory 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 volumes
mkdir -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 directory
chown -R www-data:www-data /var/www/html/storage
chmod -R 775 /var/www/html/storage
# Check if installation is fresh
if [ ! -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.sqlite
fi
# Start PHP-FPM and Nginx
echo "Starting PHP-FPM and Nginx..."
php-fpm -D
nginx -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 storage directory
  • Environment variables for security and configuration

Steps to Deploy with Docker

  1. Prepare Your Repository

    Commit and push all necessary files:

    Terminal window
    git add Dockerfile entrypoint.sh nginx.conf .dockerignore
    git commit -m "Add Docker deployment configuration for Cockpit CMS"
    git push origin main
  2. Log In to Klutch.sh Dashboard

    Go to klutch.sh/app and sign in with your GitHub account.

  3. Create a Project

    Navigate to the Projects section and create a new project for your Cockpit CMS instance.

  4. Create an App

    Click “Create App” and select your GitHub repository containing the Cockpit Docker configuration.

  5. Select the Branch

    Choose the branch you want to deploy (typically main).

  6. Configure Traffic Type

    Select HTTP as the traffic type.

  7. Set the Internal Port

    Set the internal port to 8080 – this is the port configured in Nginx.

  8. Add Environment Variables

    Configure environment variables for your Cockpit instance:

    # Application Settings
    APP_NAME=CockpitCMS
    APP_ENV=production
    COCKPIT_SESSION_NAME=cockpit_session
    COCKPIT_SALT=generate_a_long_random_string_here
    # Optional: MongoDB Configuration (if not using SQLite)
    # COCKPIT_MONGODB_URI=mongodb://user:pass@host:27017/cockpit
    # PHP Settings
    PHP_MEMORY_LIMIT=256M
    PHP_UPLOAD_MAX_FILESIZE=64M

    Note: COCKPIT_SALT is critical for security. Generate a strong random string.

  9. Configure Persistent Storage

    Cockpit stores all data (SQLite database, uploads, cache, accounts) in the storage directory. You must persist this directory to prevent data loss on redeployment.

    1. After creating the app, go to Volumes
    2. Add a volume:
      • Mount path: /var/www/html/storage
      • Size: 2-5 GiB (adjust based on expected media uploads)
  10. 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)
  11. Deploy

    Click “Create” to start deployment. Klutch.sh will:

    1. Build the Docker image
    2. Start the Cockpit container
    3. Assign a public URL (e.g., example-app.klutch.sh)
    4. Configure HTTPS automatically
  12. Initial Setup

    Once deployed, access your app URL (e.g., https://example-app.klutch.sh/install).

    1. Follow the on-screen instructions to create the initial admin account.
    2. Important: If the install page is not visible, try accessing /auth/login directly. The default credentials for a fresh install might be admin / 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:

  1. Navigate to Collections in the dashboard.
  2. Click Add Collection.
  3. Define the name (e.g., “Blog Posts”).
  4. Add fields (Text, Image, Markdown, Boolean, etc.).
  5. 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:

  1. Navigate to Singletons.
  2. Click Add Singleton.
  3. Define fields similar to Collections.
  4. Use the API to fetch this single object.

3. Forms

Cockpit can handle form submissions from your frontend.

Creating a Form:

  1. Navigate to Forms.
  2. Create a new form (e.g., “Contact”).
  3. Configure email notifications to receive alerts on submission.
  4. Post data to /api/forms/submit/Contact from 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:

  1. Go to Settings > API Access.
  2. Master API Key: Full access (keep secret).
  3. Custom Keys: Create keys with specific rules (e.g., read-only access to “Blog Posts”).
  4. Use the token in your requests: ?token=YOUR_API_KEY or header Cockpit-Token: YOUR_API_KEY.

Example Request:

Terminal window
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:

<?php
return [
// 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:

  1. Go to Settings > Webhooks.
  2. Click Add Webhook.
  3. Url: The endpoint to notify (e.g., Netlify build hook).
  4. Events: Select events to trigger the hook (e.g., collections.save.post).
  5. 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:

  1. Download the addon to your local machine.
  2. Add it to the modules/addons directory in your repository.
  3. Rebuild your Docker image or mount the addon volume.

Example Addon Structure:

/modules/addons/MyAddon/
├── admin.php
├── bootstrap.php
└── Controller/
└── Admin.php

Persistent 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:

  1. SQLite: Simply download the cockpit.sqlite file from the storage/data directory.
  2. Uploads: Backup the storage/uploads directory.
  3. MongoDB: If using MongoDB, use mongodump to backup the external database.

Security Best Practices

1. Secure the Install

  • Change Default Credentials: Immediately change the admin password 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.php if you have a Redis instance.
  • Use the Simple cache (filesystem) by default, which works well with the persistent volume.

2. Image Optimization

Cockpit can resize images on the fly.

  • Use the thumbnail API to request specific image sizes for your frontend to reduce bandwidth.
  • The Dockerfile includes imagemagick and libwebp for 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/storage is writable.
  • Ensure sqlite extension 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 in nginx.conf.
  • Verify the location / block is correctly configured.

Issue 4: Uploads Fail

Cause: File size limit exceeded. Solution:

  • Check PHP_UPLOAD_MAX_FILESIZE env var.
  • Check client_max_body_size in nginx.conf.
  • Ensure persistent volume has free space.

External Resources


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.