Skip to content

Deploying a CodeIgniter App

What is CodeIgniter?

CodeIgniter is a lightweight, open-source PHP framework that provides a rich set of libraries and a logical structure to access these libraries. It is built for developers who need a simple, elegant, and powerful toolkit to create full-featured web applications.

Key features include:

  • Lightweight and easy to understand MVC architecture
  • Built-in security features (CSRF protection, XSS filtering, SQL injection prevention)
  • Query Builder class for building database queries without writing raw SQL
  • Powerful routing system with regex support
  • RESTful API development support
  • Flexible file upload handling
  • Session and cookie management
  • Form validation library
  • Caching drivers (File, Redis, Memcached)
  • Comprehensive logging system
  • Error handling and debugging tools
  • Templating engine (PHP-based views)
  • Middleware support
  • Database migration system
  • Built-in testing frameworks
  • Email library
  • File management utilities
  • Composer dependency management
  • Extensive documentation and community

CodeIgniter is ideal for building web applications, RESTful APIs, content management systems, e-commerce platforms, real-time applications, and scalable web services.

Prerequisites

Before deploying a CodeIgniter application to Klutch.sh, ensure you have:

  • PHP 7.4 or later (PHP 8.0+ recommended)
  • Composer for dependency management
  • Git and a GitHub account
  • A Klutch.sh account with dashboard access
  • PostgreSQL or MySQL for data persistence
  • Basic understanding of PHP and MVC architecture

Getting Started with CodeIgniter

Step 1: Create a CodeIgniter Project

Using Composer, create a new CodeIgniter project:

Terminal window
composer create-project codeigniter4/appstarter codeigniter-app
cd codeigniter-app

This creates a project structure:

codeigniter-app/
├── app/
│ ├── Controllers/
│ ├── Models/
│ ├── Views/
│ ├── Config/
│ ├── Filters/
│ └── Libraries/
├── public/
│ └── index.php
├── writable/
│ ├── cache/
│ ├── logs/
│ └── uploads/
├── tests/
├── .env
├── .env.example
├── composer.json
└── spark

Step 2: Configure Environment

Create a .env file from the example:

Terminal window
cp .env.example .env

Edit .env:

# CI_ENVIRONMENT must be 'production' for live servers
CI_ENVIRONMENT = development
# This variable must be changed to something else for production!
ENCRYPTION_KEY =
# Database configuration
database.default.hostname = localhost
database.default.database = codeigniter_db
database.default.username = root
database.default.password = password
database.default.DBDriver = MySQLi
# Or for PostgreSQL:
# database.default.DBDriver = Postgre
# database.default.hostname = localhost
# database.default.port = 5432
# App settings
app.baseURL = http://localhost:8080/
app.indexPage = ''
# Session configuration
session.driver = files
session.cookieName = ci_session
# Logging
logging.threshold = 4

Step 3: Create Database Model

Create app/Models/ItemModel.php:

<?php
namespace App\Models;
use CodeIgniter\Model;
class ItemModel extends Model
{
protected $table = 'items';
protected $primaryKey = 'id';
protected $useAutoIncrement = true;
protected $returnType = 'array';
protected $useSoftDeletes = false;
protected $allowedFields = ['name', 'description', 'price', 'created_at', 'updated_at'];
protected $useTimestamps = true;
protected $dateFormat = 'datetime';
protected $createdField = 'created_at';
protected $updatedField = 'updated_at';
protected $validationRules = [
'name' => 'required|min_length[1]|max_length[100]|is_unique[items.name]',
'description' => 'permit_empty|max_length[500]',
'price' => 'required|numeric|greater_than[0]',
];
protected $validationMessages = [
'name' => [
'required' => 'Name is required',
'is_unique' => 'This item name already exists',
],
'price' => [
'required' => 'Price is required',
'numeric' => 'Price must be a number',
],
];
public function getItems($limit = 10, $offset = 0)
{
return $this->limit($limit, $offset)->findAll();
}
public function searchItems($query)
{
return $this->like('name', $query)
->orLike('description', $query)
->findAll();
}
}

Step 4: Create Database Migration

Create app/Database/Migrations/2024-01-01-000001_CreateItemsTable.php:

<?php
namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
class CreateItemsTable extends Migration
{
public function up()
{
$this->forge->addField([
'id' => [
'type' => 'INT',
'constraint' => 11,
'unsigned' => true,
'auto_increment' => true,
],
'name' => [
'type' => 'VARCHAR',
'constraint' => 100,
'unique' => true,
],
'description' => [
'type' => 'TEXT',
'null' => true,
],
'price' => [
'type' => 'INT',
'constraint' => 11,
],
'created_at' => [
'type' => 'DATETIME',
'null' => true,
],
'updated_at' => [
'type' => 'DATETIME',
'null' => true,
],
]);
$this->forge->addKey('id', true);
$this->forge->addKey('name');
$this->forge->createTable('items');
}
public function down()
{
$this->forge->dropTable('items');
}
}

Step 5: Create API Controller

Create app/Controllers/Api/ItemsController.php:

<?php
namespace App\Controllers\Api;
use CodeIgniter\RESTful\ResourceController;
use CodeIgniter\HTTP\ResponseInterface;
use App\Models\ItemModel;
class ItemsController extends ResourceController
{
protected $modelName = ItemModel::class;
protected $format = 'json';
protected $helpers = ['form'];
public function health()
{
return $this->respond([
'status' => 'healthy',
'service' => 'codeigniter-app'
], ResponseInterface::HTTP_OK);
}
public function index()
{
$page = $this->request->getVar('page') ?? 1;
$perPage = $this->request->getVar('per_page') ?? 10;
$offset = ($page - 1) * $perPage;
$items = $this->model->getItems($perPage, $offset);
$total = $this->model->countAll();
return $this->respond([
'data' => $items,
'pagination' => [
'total' => $total,
'page' => $page,
'per_page' => $perPage,
'total_pages' => ceil($total / $perPage)
]
], ResponseInterface::HTTP_OK);
}
public function show($id = null)
{
$item = $this->model->find($id);
if (!$item) {
return $this->failNotFound('Item not found');
}
return $this->respond($item, ResponseInterface::HTTP_OK);
}
public function create()
{
$data = $this->request->getJSON(true);
if (!$this->model->insert($data)) {
return $this->fail($this->model->errors(), ResponseInterface::HTTP_BAD_REQUEST);
}
$id = $this->model->getInsertID();
$item = $this->model->find($id);
return $this->respondCreated($item);
}
public function update($id = null)
{
$item = $this->model->find($id);
if (!$item) {
return $this->failNotFound('Item not found');
}
$data = $this->request->getJSON(true);
if (!$this->model->update($id, $data)) {
return $this->fail($this->model->errors(), ResponseInterface::HTTP_BAD_REQUEST);
}
return $this->respond($this->model->find($id), ResponseInterface::HTTP_OK);
}
public function delete($id = null)
{
$item = $this->model->find($id);
if (!$item) {
return $this->failNotFound('Item not found');
}
$this->model->delete($id);
return $this->respondDeleted(['id' => $id]);
}
public function stats()
{
return $this->respond([
'total_items' => $this->model->countAll(),
'timestamp' => time()
], ResponseInterface::HTTP_OK);
}
}

Step 6: Configure Routes

Edit app/Config/Routes.php:

<?php
namespace Config;
use CodeIgniter\Router\RouteCollection;
/**
* @var RouteCollection $routes
*/
$routes->setDefaultNamespace('App\Controllers');
// Health check
$routes->get('health', 'Home::health');
// API Routes
$routes->group('api', ['namespace' => 'App\Controllers\Api'], function ($routes) {
$routes->get('items/health', 'ItemsController::health');
$routes->get('items/stats', 'ItemsController::stats');
$routes->resource('items', ['controller' => 'ItemsController']);
});
$routes->get('/', 'Home::index');

Step 7: Create Home Controller

Edit app/Controllers/Home.php:

<?php
namespace App\Controllers;
class Home extends BaseController
{
public function health()
{
return $this->response->setJSON([
'status' => 'healthy',
'service' => 'codeigniter-app'
]);
}
public function index()
{
return view('welcome_message');
}
}

Step 8: Update Composer Dependencies

Edit composer.json to ensure required packages:

{
"name": "codeigniter4/appstarter",
"type": "project",
"description": "CodeIgniter4 Application Starter",
"require": {
"php": "^7.4 || ^8.0",
"codeigniter4/framework": "^4.4"
},
"require-dev": {
"mikey179/phpunit-mock-objects": "^3.0",
"phpunit/phpunit": "^9.1"
}
}

Step 9: Build and Test Locally

Terminal window
# Install dependencies
composer install
# Create .env file with database configuration
cp .env.example .env
# Run database migrations
php spark migrate
# Start the development server
php spark serve
# Test the API
curl http://localhost:8080/api/items/health

Deploying Without a Dockerfile

Klutch.sh uses Nixpacks to automatically detect and build your CodeIgniter application from your source code.

Prepare Your Repository

  1. Initialize a Git repository and commit your code:
Terminal window
git init
git add .
git commit -m "Initial CodeIgniter app commit"
  1. Create a .gitignore file:
.env
.env.production
.env.local
.vscode/
.idea/
writable/cache/*
writable/logs/*
writable/uploads/*
vendor/
composer.lock
.DS_Store
*.log
node_modules/
  1. Update your .env.example with all configuration variables:
CI_ENVIRONMENT = production
ENCRYPTION_KEY =
app.baseURL = https://example-app.klutch.sh/
database.default.hostname = {DB_HOSTNAME}
database.default.database = {DB_DATABASE}
database.default.username = {DB_USERNAME}
database.default.password = {DB_PASSWORD}
  1. Ensure your project structure includes:

    • public/index.php as the entry point
    • composer.json for dependencies
    • .env.example for configuration template
  2. Push to GitHub:

Terminal window
git remote add origin https://github.com/YOUR_USERNAME/codeigniter-app.git
git branch -M main
git push -u origin main

Deploy to Klutch.sh

  1. Log in to Klutch.sh dashboard.

  2. Click “Create a new project” and provide a project name.

  3. Inside your project, click “Create a new app”.

  4. Repository Configuration:

    • Select your GitHub repository containing the CodeIgniter app
    • Select the branch to deploy (typically main)
  5. Traffic Settings:

    • Select “HTTP” as the traffic type
  6. Port Configuration:

    • Set the internal port to 8080 (the recommended port for PHP applications)
  7. Environment Variables: Set the following environment variables in the Klutch.sh dashboard:

    • CI_ENVIRONMENT: Set to production for live deployments
    • database.default.hostname: Your database host
    • database.default.database: Your database name
    • database.default.username: Your database user
    • database.default.password: Your database password
    • ENCRYPTION_KEY: A strong random encryption key for CodeIgniter
    • app.baseURL: Your deployed app URL (e.g., https://example-app.klutch.sh/)
  8. Build and Start Commands (Optional): If you need to customize the build or start command, set these environment variables:

    • BUILD_COMMAND: Default runs composer install && php spark migrate
    • START_COMMAND: Default is php spark serve --host 0.0.0.0 --port 8080
  9. Region, Compute, and Instances:

    • Choose your desired region for optimal latency
    • Select compute resources (Starter for prototypes, Pro/Premium for production)
    • Set the number of instances (start with 1-2, scale as needed based on traffic)
  10. Click “Create” to deploy. Klutch.sh will automatically build your application using Nixpacks and deploy it.

  11. Once deployment completes, your app will be accessible at example-app.klutch.sh.

Verifying the Deployment

Test your deployed app:

Terminal window
curl https://example-app.klutch.sh/health

You should receive:

{
"status": "healthy",
"service": "codeigniter-app"
}

Access the API:

https://example-app.klutch.sh/api/items

Deploying With a Dockerfile

If you prefer more control over your build environment, you can provide a custom Dockerfile. Klutch.sh automatically detects and uses a Dockerfile in your repository’s root directory.

Create a Dockerfile

Create a Dockerfile in your project root:

# Build stage
FROM composer:2.6 as builder
WORKDIR /app
# Copy composer files
COPY composer.json composer.lock ./
# Install dependencies
RUN composer install --no-dev --optimize-autoloader
# Runtime stage
FROM php:8.2-fpm-alpine
WORKDIR /app
# Install required PHP extensions
RUN apk add --no-cache \
postgresql-dev \
mysql-dev \
nginx \
curl
RUN docker-php-ext-install \
pdo \
pdo_mysql \
pdo_pgsql \
mysqli
# Copy application from builder
COPY --from=builder /app/vendor ./vendor
COPY --chown=www-data:www-data . .
# Create necessary directories
RUN mkdir -p writable/cache writable/logs writable/uploads && \
chown -R www-data:www-data writable public
# Configure Nginx
RUN echo "server { \
listen 8080; \
server_name _; \
root /app/public; \
index index.php; \
client_max_body_size 100M; \
location / { \
try_files \$uri \$uri/ /index.php?\$query_string; \
} \
location ~ \.php\$ { \
fastcgi_pass 127.0.0.1:9000; \
fastcgi_index index.php; \
include fastcgi_params; \
fastcgi_param SCRIPT_FILENAME \$document_root\$fastcgi_script_name; \
} \
}" > /etc/nginx/conf.d/default.conf
# Health check
HEALTHCHECK --interval=30s --timeout=10s --start-period=30s --retries=3 \
CMD curl -f http://localhost:8080/health || exit 1
# Expose port
EXPOSE 8080
# Create non-root user
RUN addgroup -g 1000 codeigniter && \
adduser -D -u 1000 -G codeigniter codeigniter
USER codeigniter
# Start application
CMD ["sh", "-c", "php-fpm & nginx -g 'daemon off;'"]

Deploy the Dockerfile Version

  1. Push your code with the Dockerfile to GitHub:
Terminal window
git add Dockerfile
git commit -m "Add Dockerfile for custom build"
git push
  1. Log in to Klutch.sh dashboard.

  2. Create a new app:

    • Select your GitHub repository and branch
    • Set traffic type to “HTTP”
    • Set the internal port to 8080
    • Add environment variables
    • Click “Create”
  3. Klutch.sh will automatically detect your Dockerfile and use it for building and deployment.


Database Configuration

PostgreSQL Setup

PostgreSQL is recommended for CodeIgniter applications. Configure in .env:

database.default.DBDriver = Postgre
database.default.hostname = {DB_HOSTNAME}
database.default.port = 5432
database.default.database = {DB_DATABASE}
database.default.username = {DB_USERNAME}
database.default.password = {DB_PASSWORD}

MySQL Setup

Configure MySQL in .env:

database.default.DBDriver = MySQLi
database.default.hostname = {DB_HOSTNAME}
database.default.port = 3306
database.default.database = {DB_DATABASE}
database.default.username = {DB_USERNAME}
database.default.password = {DB_PASSWORD}

Running Migrations

Migrations run automatically on deployment or manually with:

Terminal window
php spark migrate
php spark seed:run

Configure Sessions

Edit app/Config/Session.php:

<?php
namespace Config;
use CodeIgniter\Config\BaseConfig;
use CodeIgniter\Session\Handlers\DatabaseHandler;
use CodeIgniter\Session\Handlers\FileHandler;
class Session extends BaseConfig
{
public string $sessionDriver = 'CodeIgniter\Session\Handlers\FileHandler';
// Or use database handler
// public string $sessionDriver = 'CodeIgniter\Session\Handlers\DatabaseHandler';
public string $sessionCookieName = 'ci_session';
public int $sessionExpiration = 7200;
public bool $sessionSavePath = WRITABLEPATH . 'session';
public bool $sessionMatchIP = false;
public bool $sessionTimeToUpdate = 300;
public bool $sessionRegenerateDestroy = true;
}

Session Usage

$session = session();
// Set session data
$session->set('user_id', $userId);
$session->set(['username' => $username, 'email' => $email]);
// Get session data
$userId = $session->get('user_id');
$userData = $session->getFlashdata('user_data');
// Remove session data
$session->remove('user_id');

File Uploads

Configure Uploads

Edit app/Config/Files.php:

<?php
namespace Config;
use CodeIgniter\Config\BaseConfig;
class Files extends BaseConfig
{
public int $fileSize = 100;
public int $uploadDir = WRITABLEPATH . 'uploads';
}

Handle File Uploads

public function uploadFile()
{
$file = $this->request->getFile('file');
if ($file->isValid() && !$file->hasMoved()) {
$newName = $file->getRandomName();
$file->move(WRITABLEPATH . 'uploads', $newName);
return $this->respond([
'message' => 'File uploaded successfully',
'filename' => $newName,
'size' => $file->getSize()
]);
}
return $this->fail('File upload failed');
}

Caching Configuration

Redis Caching

Configure Redis in app/Config/Cache.php:

<?php
namespace Config;
use CodeIgniter\Config\BaseConfig;
class Cache extends BaseConfig
{
public string $handler = 'redis';
public array $redis = [
'host' => getenv('REDIS_HOST') ?? 'localhost',
'password' => getenv('REDIS_PASSWORD') ?? null,
'port' => getenv('REDIS_PORT') ?? 6379,
'timeout' => 0,
'database' => 0,
];
public int $ttl = 3600;
}

Using Cache

$cache = cache();
// Save to cache
$cache->save('items', $items, 3600);
// Retrieve from cache
$items = $cache->get('items');
// Delete from cache
$cache->delete('items');

Form Validation

Validate Form Data

public function store()
{
if (!$this->validate([
'name' => 'required|min_length[3]|max_length[100]',
'email' => 'required|valid_email',
'age' => 'required|numeric|greater_than[0]',
])) {
return $this->fail($this->validator->getErrors());
}
$data = $this->request->getPost();
$this->itemModel->insert($data);
return $this->respondCreated($data);
}

Logging and Error Handling

Configure Logging

Edit app/Config/Logger.php:

<?php
namespace Config;
use CodeIgniter\Config\BaseConfig;
use CodeIgniter\Log\Handlers\FileHandler;
class Logger extends BaseConfig
{
public string $logPath = WRITABLEPATH . 'logs/';
public string $threshold = 3;
public array $handlers = [
'CodeIgniter\Log\Handlers\FileHandler' => [
'handles' => ['critical', 'alert', 'emergency', 'error', 'warning', 'notice', 'info', 'debug'],
],
];
}

Use Logging

$logger = service('logger');
$logger->info('Item created: ' . $itemId);
$logger->error('Database error occurred', ['error' => $error]);
$logger->debug('Debug information', $debugData);

Persistent Storage for Uploads and Logs

Adding Persistent Volume

  1. In the Klutch.sh app dashboard, navigate to “Persistent Storage” or “Volumes”
  2. Click “Add Volume”
  3. Set the mount path: /app/writable (for uploads and cache/logs)
  4. Set the size based on your needs (e.g., 20 GB for uploads and logs)
  5. Save and redeploy

Configure Writable Paths

The writable directory should be mounted as persistent storage:

Terminal window
# Ensure writable directory permissions
chmod -R 755 writable
chmod -R 777 writable/cache writable/logs writable/uploads

Custom Domains

To serve your CodeIgniter application from a custom domain:

  1. In the Klutch.sh app dashboard, navigate to “Custom Domains”
  2. Click “Add Custom Domain”
  3. Enter your domain (e.g., api.example.com)
  4. Follow the DNS configuration instructions provided

Example DNS configuration:

api.example.com CNAME example-app.klutch.sh

Troubleshooting

Issue 1: Database Connection Error

Problem: “Unable to connect to database” during deployment.

Solution:

  • Verify database connection credentials in environment variables
  • Ensure PostgreSQL/MySQL instance is running and accessible
  • Check database user has proper permissions
  • Test connection string format
  • Verify firewall rules allow the connection

Issue 2: File Upload Permission Denied

Problem: File uploads fail with permission denied errors.

Solution:

  • Mount persistent storage for writable directory
  • Set proper directory permissions: chmod 777 writable/uploads
  • Ensure web server user has write permissions
  • Check disk space availability
  • Verify file size limits

Issue 3: Session Data Lost

Problem: Session data is not persisting between requests.

Solution:

  • Configure persistent storage for session files
  • Use database session handler for distributed systems
  • Verify session cookie settings in config
  • Check browser cookie settings
  • Ensure session path is writable

Issue 4: Slow Application Performance

Problem: Application responds slowly to requests.

Solution:

  • Enable caching for frequently accessed data
  • Optimize database queries and add indexes
  • Use Redis for session and cache storage
  • Enable query result caching
  • Monitor performance metrics in dashboard
  • Consider scaling to additional instances

Issue 5: Memory or Disk Space Issues

Problem: Application crashes due to memory or disk space limits.

Solution:

  • Increase instance compute resources
  • Increase persistent storage size
  • Optimize database queries
  • Clean up old log files
  • Implement file cleanup routines
  • Monitor resource usage via dashboard

Best Practices for Production Deployment

  1. Environment Configuration: Use .env for sensitive data

    CI_ENVIRONMENT = production
    database.default.password = {STRONG_PASSWORD}
    ENCRYPTION_KEY = {RANDOM_KEY}
  2. Enable HTTPS: Always use HTTPS in production

    app.forceGlobalSecureRequests = true;
  3. Input Validation: Validate all user inputs

    $this->validate(['name' => 'required|min_length[3]']);
  4. SQL Injection Prevention: Use Query Builder or prepared statements

    $this->db->select('*')->from('items')->where('id', $id)->get();
  5. CSRF Protection: Enable CSRF protection (enabled by default)

    filters => ['csrf']
  6. Error Handling: Don’t expose sensitive error details

    if (!$item) {
    return $this->failNotFound('Resource not found');
    }
  7. Database Optimization: Add indexes to frequently queried columns

    CREATE INDEX idx_name ON items(name);
  8. Session Security: Use secure session configuration

    sessionSavePath = '/tmp/sessions';
    sessionRegenerateDestroy = true;
  9. Logging: Log important events and errors

    $logger->info('User login: ' . $userId);
  10. Rate Limiting: Implement rate limiting for APIs

    if ($this->request->getIPAddress() === $cachedIP) {
    return $this->fail('Too many requests', 429);
    }

Resources


Conclusion

Deploying CodeIgniter applications to Klutch.sh provides a lightweight, flexible platform for building and running PHP web applications. CodeIgniter’s simplicity, security features, and powerful tools make it ideal for developers building custom solutions.

Key takeaways:

  • Use Nixpacks for quick deployments with automatic PHP detection
  • Use Docker for complete control over PHP extensions and dependencies
  • Leverage CodeIgniter’s Query Builder for safe database queries
  • Implement proper form validation for all user inputs
  • Use caching strategies to improve performance
  • Configure persistent storage for file uploads and logs
  • Enable HTTPS and CSRF protection in production
  • Implement structured logging for monitoring and debugging
  • Use environment variables for configuration management
  • Monitor application health and performance metrics

For additional help, refer to the CodeIgniter documentation or Klutch.sh support resources.