Back to Blog
Tutorial10 min read2026-06-28

How to Deploy a CodeIgniter PHP App to Production

Deploy CodeIgniter 4 to production the right way: a lean Dockerfile, correct base URL and environment handling, database setup, and the security flags most tutorials forget.

Ajay Kumar
Ajay Kumar
Founder & DevOps, PandaStack

CodeIgniter is the lightweight PHP framework people reach for when they want MVC structure without the ceremony of Symfony or Laravel. It's fast, has a tiny footprint, and its docs are excellent. But "lightweight" can lull you into deploying it the wrong way — copying files over FTP, leaving CI_ENVIRONMENT on development, and exposing the whole project root. This guide covers a clean container deployment of CodeIgniter 4.

Why containerize CodeIgniter

CodeIgniter 4 has a clear separation between the framework, your app, and the public web root. In production you want:

  • Only public/ exposed as the document root.
  • CI_ENVIRONMENT=production so detailed error pages are suppressed.
  • php-fpm + nginx (or FrankenPHP) instead of php spark serve, which is a dev-only server.
  • Dependencies installed with Composer, not committed vendor/.

The directory layout

my-ci-app/
├── app/
├── public/
│   └── index.php
├── system/
├── writable/
├── composer.json
├── env
└── spark

The public/ directory is your document root. Everything else — including app/ and the env file — must stay outside the web-accessible path. Pointing your server at the project root instead of public/ is the most common CodeIgniter security mistake.

Production Dockerfile

FROM php:8.3-fpm-alpine AS app

RUN apk add --no-cache nginx \
 && docker-php-ext-install pdo_mysql opcache intl

COPY --from=composer:2 /usr/bin/composer /usr/bin/composer

WORKDIR /var/www/html

COPY composer.json composer.lock ./
RUN composer install --no-dev --optimize-autoloader --no-scripts

COPY . .

# nginx config points root at /var/www/html/public
COPY docker/nginx.conf /etc/nginx/http.d/default.conf
COPY docker/start.sh /start.sh
RUN chmod +x /start.sh && chmod -R 775 writable

ENV CI_ENVIRONMENT=production
EXPOSE 8080
CMD ["/start.sh"]

The nginx config is short but matters:

server {
    listen 8080;
    root /var/www/html/public;
    index index.php;

    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;
    }
}

The start.sh script launches php-fpm and nginx together:

#!/bin/sh
php-fpm -D
nginx -g 'daemon off;'

Configuration via environment variables

CodeIgniter reads from an env file, but in production you should set real environment variables instead. The framework maps dotted config keys to env vars. Key ones:

Env varPurpose
CI_ENVIRONMENTproduction — disables debug, hides errors
app.baseURLYour public URL, e.g. https://app.example.com/
app.indexPageEmpty string to drop index.php from URLs
database.default.hostnameDB host
database.default.databaseDB name
database.default.username / .passwordCredentials
database.default.DBDriverMySQLi or Postgre
encryption.keyRequired for sessions/encryption

Set app.baseURL correctly — if it's wrong, asset URLs and redirects break. Behind an HTTPS-terminating ingress, set it to your https:// domain explicitly because CodeIgniter won't infer the scheme reliably from forwarded headers.

Wiring up the database

With a managed MySQL 8 instance, your app gets connection details injected. CodeIgniter's database config reads them from env vars:

// app/Config/Database.php — defaults are overridden by env
public array $default = [
    'hostname' => '',
    'username' => '',
    'password' => '',
    'database' => '',
    'DBDriver' => 'MySQLi',
];

If your platform injects a single DATABASE_URL, parse it in Database.php or map the individual database.default.* vars. On PandaStack you can create a MySQL database, link it, and set these env vars from the connection details shown in the dashboard.

Run migrations as a release step:

php spark migrate --all

The encryption key

CodeIgniter needs an encryption key for sessions and the encryption service. Generate one and set it as encryption.key:

php spark key:generate

In a containerized deploy, don't rely on key:generate writing to the env file (the filesystem is ephemeral). Instead, generate the key once locally and store it as a secret env var so it stays stable across deploys. A changing key invalidates all sessions on every release.

Performance flags

  • OPcache: enabled via docker-php-ext-install opcache. Add opcache.validate_timestamps=0 in production so PHP doesn't stat files on every request.
  • composer install --optimize-autoloader: builds a classmap.
  • writable/ permissions: must be writable (775) for cache, logs, and sessions, but keep it out of the web root — it already is by default.

Deploying

Push, connect the repo, link the database, set env vars. The platform builds your Dockerfile, deploys it, and provisions SSL for your custom domain. Live logs will surface any writable/ permission issues or missing extensions immediately.

git push origin main

Conclusion

CodeIgniter rewards a disciplined deploy: serve only public/, flip CI_ENVIRONMENT to production, set a stable encryption key, and treat migrations as a release step. Do those four things and the framework's natural speed shines.

Want to try it without configuring servers? PandaStack's free tier includes a container service and a managed database — connect your CodeIgniter repo at [dashboard.pandastack.io](https://dashboard.pandastack.io) and it builds and deploys automatically.

References

  • [CodeIgniter 4: Running Your App](https://codeigniter.com/user_guide/general/managing_apps.html)
  • [CodeIgniter 4: Handling Multiple Environments](https://codeigniter.com/user_guide/general/environments.html)
  • [CodeIgniter 4: Database Configuration](https://codeigniter.com/user_guide/database/configuration.html)
  • [CodeIgniter 4: Encryption Service](https://codeigniter.com/user_guide/libraries/encryption.html)
  • [PHP OPcache Configuration](https://www.php.net/manual/en/opcache.configuration.php)

Ready to deploy?

Start free on PandaStack.

Start free on PandaStack

More in Tutorial

Browse all Tutorial articles →

See also