Building with AI – A Developer's Diary
A Local LAMP Stack with Docker and CodeIgniter
No MAMP. No global PHP install. Just Docker, a Dockerfile, and a clean development environment you can rebuild from scratch in five minutes.
There is a particular kind of frustration that comes from your local development environment being more complicated than the project you are trying to build. You want to write a controller. Instead you spend an hour reading about PHP version conflicts, Apache configs that only work on one machine, or a MAMP setup that broke when you updated macOS. The tooling is supposed to disappear. When it does not, it is the only thing you can think about.
Docker fixes this—not by being simple, but by being consistent. Once you have a working setup, it works everywhere, every time, on any machine. This walkthrough builds a full local LAMP stack with CodeIgniter 4 from scratch. By the end you have a running server at http://localhost:8080, a MySQL database, and a workflow for starting the day that takes about ten seconds.
1. Create the project folder
Start with an empty directory. Everything—the app, the Dockerfile, the Compose file—lives here.
Shellmkdir LAMP
cd LAMP
2. Install CodeIgniter
You do not need PHP installed locally. Docker has it. Use a throwaway Composer container to scaffold the CodeIgniter project into a subfolder called app.
docker run --rm -v "$PWD":/app -w /app composer:2 create-project codeigniter4/appstarter app
That creates the CodeIgniter skeleton, but it does not install the vendor dependencies inside it. Run a second pass to do that:
Shelldocker run --rm -v "$PWD/app":/app -w /app composer:2 install --ignore-platform-req=ext-intl
This step is easy to miss, and the error it produces is misleading. If you skip it, you will eventually see something like:
ShellFailed opening required '/var/www/html/vendor/codeigniter4/framework/system/Boot.php'
The message sounds like CodeIgniter is missing. It is not—the framework is there, but its vendor/ folder is empty. Running composer install fills it in, and the error goes away.
3. Create the Dockerfile
In the LAMP folder—not inside app—create the Dockerfile. This defines the PHP and Apache environment your container will run.
FROM php:8.4-apache
RUN apt-get update \
&& apt-get install -y libicu-dev zip unzip \
&& docker-php-ext-install intl mysqli pdo_mysql \
&& a2enmod rewrite
COPY docker/apache/default.conf /etc/apache2/sites-available/000-default.conf
WORKDIR /var/www/html
Three things worth noting here. First, docker-php-ext-install intl handles the platform requirement that made you pass --ignore-platform-req in step 2—the container has it, your local machine does not. Second, a2enmod rewrite enables Apache's mod_rewrite, which CodeIgniter needs for clean URLs. Third, the Dockerfile copies an Apache virtual host config you have not created yet—that comes next.
4. Configure Apache
Create the directory and config file:
Shellmkdir -p docker/apache
Then create docker/apache/default.conf:
<VirtualHost *:80>
ServerName localhost
DocumentRoot /var/www/html/public
<Directory /var/www/html/public>
AllowOverride All
Require all granted
</Directory>
ErrorLog ${APACHE_LOG_DIR}/error.log
CustomLog ${APACHE_LOG_DIR}/access.log combined
</VirtualHost>
The key line is DocumentRoot /var/www/html/public. CodeIgniter's entry point is public/index.php, not the project root. If you point Apache at the root, requests will hit the wrong place and you will get a blank page or a 403. Point it at public/ and everything routes correctly.
5. Create the Compose file
Create docker-compose.yml in the LAMP folder. This defines two services—the web server and a MySQL database—and wires them together.
services:
web:
build: .
container_name: ci_web
ports:
- "8080:80"
volumes:
- ./app:/var/www/html
depends_on:
- db
db:
image: mysql:8.4
container_name: ci_db
restart: unless-stopped
environment:
MYSQL_DATABASE: codeigniter
MYSQL_USER: ci
MYSQL_PASSWORD: ci_secret
MYSQL_ROOT_PASSWORD: root_secret
ports:
- "3306:3306"
volumes:
- db_data:/var/lib/mysql
volumes:
db_data:
The volumes mount on the web service maps your local ./app folder into the container at /var/www/html. This is what makes live editing work—you change a file in your editor, reload the browser, the container sees it immediately. No rebuild required for code changes.
6. Configure CodeIgniter
CodeIgniter reads environment settings from a .env file. Create one from the template:
cd app
cp env .env
Open .env and set the following values. The database hostname is db—the name of the service in docker-compose.yml, not localhost. Docker's internal networking resolves service names automatically.
CI_ENVIRONMENT = development
app.baseURL = 'http://localhost:8080/'
database.default.hostname = db
database.default.database = codeigniter
database.default.username = ci
database.default.password = ci_secret
database.default.DBDriver = MySQLi
database.default.port = 3306
Then step back up to the project root:
Shellcd ..
7. Start the server
Build the image and start the containers:
Shelldocker compose up -d --build
The first build takes a minute or two while Docker pulls the base image and installs PHP extensions. Subsequent starts are much faster. When it finishes, open a browser and go to:
Shellhttp://localhost:8080
You should see the CodeIgniter welcome page. If you do, the stack is working—Apache is serving CodeIgniter from the correct document root, PHP has its extensions, and the database is up and waiting.
Coming back the next day
This is the part that makes Docker worth the setup cost. Tomorrow morning, you do not reinstall anything. You do not check PHP versions or restart MAMP or remember which port your local database uses. You just come back to the project folder and bring the containers up.
Shellcd LAMP
docker compose up -d
That is it. The containers start in the background—usually in two or three seconds—and your environment is exactly as you left it. If you want to confirm CodeIgniter is awake before you start writing code, you can run:
Shelldocker compose exec web php spark --version
Then open your editor, change a controller, refresh the browser. The project is ready. Coffee is optional, but spiritually recommended.
Project structure at a glance
When everything is in place, your LAMP folder looks like this:
LAMP/
├── app/ # CodeIgniter project
│ ├── .env
│ ├── vendor/
│ └── public/
│ └── index.php
├── docker/
│ └── apache/
│ └── default.conf
├── Dockerfile
└── docker-compose.yml
The app/ folder is your working directory. Everything else is infrastructure—the files that build and configure the containers. You mostly ignore them once they're working.
docker compose down stops and removes the containers. Your code and database data are preserved—code in ./app, database in the db_data Docker volume. Run docker compose up -d to bring it back.