# Technical documentation — indawo_docker

Evidence date: 2026-10-12. Revision: `main` `76c21e36f6668cdb4c4d9947a1c00afc57df5a4a` (2025-03-08 10:52:45 +0200, author Ettiene `ettiene@overdrive.co.za`, subject `updating container`).

Remote: `git@bitbucket.org:overdrive/indawo_docker.git`. `origin/HEAD` is `main`. `git ls-remote --heads --tags` returned that one ref and no tags.

The scan-host path `~/projects/sonar_scan/repos/indawo_docker` was not on the machine that wrote this document. Findings are from a clone of `76c21e3`, which is the commit the onboarding issue recorded.

## Architecture

Docker Compose stack for local PHP development. No application framework is vendored. The web container is PHP 5.6 with Apache (`mod_php` via the official `php:5.6-apache` image). MySQL 5.7, phpMyAdmin, and Redis are sibling services.

```text
developer host
  .env  (copied from sample.env, gitignored)
  docker compose
    webserver   build ./bin/${PHPVERSION}     ports 80 and 443
                mount ./www → /var/www/html
                link hostname "database"
    database    build ./bin/${DATABASE}       127.0.0.1:3306
                mount ./data/mysql → /var/lib/mysql
    phpmyadmin  image phpmyadmin              ports 8080 and 8443 (sample)
                PMA_HOST=database
    redis       image redis:latest            127.0.0.1:6379
```

With `sample.env`, `${PHPVERSION}` is `php56` and `${DATABASE}` is `mysql57`.

There is no background worker, queue, or scheduler. Redis is only declared. No PHP Redis extension is installed, and nothing in the repo reads from Redis.

## Repository layout

```text
.gitignore
LICENSE
README.md
bin/mysql57/Dockerfile
bin/php56/Dockerfile
bin/php56/Dockerfile2
dev_build_container.sh
docker-compose.yml
sample.env
todo.md
```

| Path | Role |
|---|---|
| `docker-compose.yml` | Four services. Compose file version `"3"` |
| `sample.env` | Committed template for `.env` |
| `bin/php56/Dockerfile` | Image Compose builds for the web service when `PHPVERSION=php56` |
| `bin/php56/Dockerfile2` | Second PHP 5.6 definition. No `dockerfile:` key points at it |
| `bin/mysql57/Dockerfile` | `FROM mysql:5.7` only |
| `dev_build_container.sh` | Linux matrix build. Uses the `docker-compose` binary, not `docker compose` |
| `README.md` | Upstream sprintcube LAMP instructions, including a GitHub clone URL |
| `LICENSE` | MIT. Copyright Narendra Vaghela (2017) and Malte Graebner (2019) |
| `todo.md` | Two bullets: redesign the README, update issue templates |
| `.gitignore` | Stock Atlassian example list, plus project ignores added in `76c21e3` |

Ignored and therefore absent from the commit: `/.env`, `/.vscode`, `/config/*`, `/data/*`, `/logs/*`, `/www/**`, `/buildtest`.

## Web image (`bin/php56/Dockerfile`)

Base: `php:5.6-apache`.

Package feed. The Dockerfile copies `/etc/apt/sources.list` to `sources.list.old`, truncates it, and writes Debian Stretch lines for `archive.debian.org` (`stretch main` and `stretch/updates` security). It then runs `apt-get update` and `apt-get upgrade -y`.

Packages named in the install step: `nano`, `wget`, `dialog`, `build-essential`, `git`, `curl`, `libcurl3`, `libcurl3-dev`, `zip`, `libmcrypt-dev`, `libsqlite3-dev`, `libsqlite3-0`, `mysql-client`, `zlib1g-dev`, `libicu-dev`, `libfreetype6-dev`, `libjpeg62-turbo-dev`, `libpng-dev`, `unixodbc`, `unixodbc-dev`, `libodbc1`, `odbcinst`, `odbcinst1debian2`, `freetds-bin`, `freetds-dev`, `tdsodbc`. Apt lists are removed after that layer. A later ODBC layer runs `apt-get update` again and installs `wget` again.

Composer: `curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer`.

FreeTDS: appends a section `[mssql]` to `/etc/freetds/freetds.conf` with `host = your_sql_server_host`, `port = 1433`, `tds version = 7.4`.

ODBC driver registration: appends `[FreeTDS]` to `/etc/odbcinst.ini` with driver `/usr/lib/x86_64-linux-gnu/odbc/libtdsodbc.so` and setup `/usr/lib/x86_64-linux-gnu/odbc/libtdsS.so`.

ODBC PHP extension: downloads `https://www.php.net/distributions/php-5.6.40.tar.gz`, unpacks it, symlinks `sql.h`, `sqlext.h`, `sqltypes.h`, and `odbcinst.h` into `/usr/local/incl`, comments out `#include <WINDOWS.H>` in `ext/odbc/php_odbc_includes.h`, then `phpize` and `./configure --with-unixODBC=/usr --with-odbcver=0x0350` with `LDFLAGS="-L/usr/lib/x86_64-linux-gnu -lodbc"`. It installs the result and writes `extension=odbc.so` to `/usr/local/etc/php/conf.d/odbc.ini`. The unpacked PHP source tree is deleted. The tarball directory `/usr/src/php` is not fully removed; the recipe removes `/usr/src/php/php-5.6.40` only.

Other PHP extensions, via `docker-php-ext-install`: `curl`, `tokenizer`, `json`, `bcmath`, `mcrypt`, `pdo_mysql`, `pdo_sqlite`, `mysqli`, `zip`, `intl`, `mbstring`, `gd` configured with `--with-freetype-dir=/usr/include/` and `--with-jpeg-dir=/usr/include/`. Then `pecl install xdebug-2.5.5`, `docker-php-ext-enable xdebug`, and `echo "xdebug.remote_enable=1" >> /usr/local/etc/php/php.ini`.

Apache: `mkdir -p /etc/apache2/ssl`, `a2enmod ssl`, `a2enmod rewrite`, `a2enmod headers`, `ServerName localhost` appended to `apache2.conf`. A default `/var/www/html/index.php` contains `<?php phpinfo();`.

Process: `EXPOSE 80` and `EXPOSE 443`. `ENTRYPOINT ["/usr/sbin/apache2ctl", "-D", "FOREGROUND"]`. There is no `USER` instruction, so the image keeps the upstream default user. That user was not inspected beyond the Dockerfile text.

## Alternate web image (`bin/php56/Dockerfile2`)

Same base, same Stretch archive rewrite, and the same apt line except it stops before `unixodbc` / FreeTDS. Same Composer install. Same `docker-php-ext-install` list and the same Xdebug 2.5.5 lines. It creates `/etc/apache2/ssl`, runs `a2enmod ssl && a2enmod rewrite`, then `a2enmod rewrite headers`. It does not set `ServerName`, does not write `index.php`, and does not build ODBC.

Compose build `context: ./bin/${PHPVERSION}` uses the default Dockerfile name. `Dockerfile2` is unused by the compose file.

## Database image

`bin/mysql57/Dockerfile` is a single line: `FROM mysql:5.7`.

Accounts, database name, and the data directory come from Compose environment and volumes, not from this Dockerfile. Official image entrypoint behaviour (init files in `/docker-entrypoint-initdb.d` on first start) is what the README describes. That behaviour is upstream image behaviour, not a script in this repo. Provenance of the README sentence: **Documented**. Provenance of an init script in this repo: none committed.

## Compose services

### `webserver`

| Item | Value |
|---|---|
| Build | `./bin/${PHPVERSION}` |
| Container name | `${COMPOSE_PROJECT_NAME}-${PHPVERSION}` |
| Restart | `always` |
| Ports | `${HOST_MACHINE_UNSECURE_HOST_PORT}:80`, `${HOST_MACHINE_SECURE_HOST_PORT}:443` |
| Links | `database` |
| Extra hosts | `host.docker.internal:host-gateway` |

Environment passed into the container: `APACHE_DOCUMENT_ROOT` (default `/var/www/html`), `PMA_PORT` (from `HOST_MACHINE_PMA_PORT`), `MYSQL_ROOT_PASSWORD`, `MYSQL_USER`, `MYSQL_PASSWORD`, `MYSQL_DATABASE`, `HOST_MACHINE_MYSQL_PORT`, and `XDEBUG_CONFIG` (`client_host=host.docker.internal remote_port=${XDEBUG_PORT}`).

The Dockerfiles do not read `PMA_PORT`, `MYSQL_*`, or `HOST_MACHINE_MYSQL_PORT`. They are available to whatever PHP is mounted later. Provenance: **Implemented** in Compose. Use by this repo's images: **not referenced**.

Volumes:

```text
${DOCUMENT_ROOT-./www}:/var/www/html:rw
-./filesystem:/var/www/filesystem
${PHP_INI-./config/php/php.ini}:/usr/local/etc/php/php.ini
${FileSyem-./config/php/php.ini}:/usr/local/etc/php/php.ini
${SSL_DIR-./config/ssl}:/etc/apache2/ssl/
${VHOSTS_DIR-./config/vhosts}:/etc/apache2/sites-enabled
${LOG_DIR-./logs/apache2}:/var/log/apache2
${XDEBUG_LOG_DIR-./logs/xdebug}:/var/log/xdebug
```

`FileSyem` is the spelling in the file. Both ini mounts target `/usr/local/etc/php/php.ini`. The image also appends the Xdebug line to that same path before the mount exists. When the bind mount is present, the container sees the host file instead of the image file. Provenance: **Inferred** from Compose mount rules. Not run.

The volume token `-./filesystem` is a single list item whose text starts with `-./`. Provenance: **Implemented** as YAML text.

### `database`

| Item | Value |
|---|---|
| Build | `./bin/${DATABASE}` |
| Container name | `${COMPOSE_PROJECT_NAME}-${DATABASE}` |
| Restart | `always` |
| Port | `127.0.0.1:${HOST_MACHINE_MYSQL_PORT}:3306` |
| Environment | `MYSQL_ROOT_PASSWORD`, `MYSQL_DATABASE`, `MYSQL_USER`, `MYSQL_PASSWORD` |
| Volumes | initdb → `/docker-entrypoint-initdb.d`, data → `/var/lib/mysql`, logs → `/var/log/mysql` |

### `phpmyadmin`

| Item | Value |
|---|---|
| Image | `phpmyadmin` (no tag) |
| Container name | `${COMPOSE_PROJECT_NAME}-phpmyadmin` |
| Links | `database` |
| Environment | `PMA_HOST=database`, `PMA_PORT=3306`, `PMA_USER=root`, `PMA_PASSWORD=${MYSQL_ROOT_PASSWORD}`, plus `MYSQL_ROOT_PASSWORD`, `MYSQL_USER`, `MYSQL_PASSWORD`, `UPLOAD_LIMIT`, `MEMORY_LIMIT` |
| Ports | `${HOST_MACHINE_PMA_PORT}:80`, `${HOST_MACHINE_PMA_SECURE_PORT}:443` |
| Volumes | anonymous volume at `/sessions`; host php.ini mounted at `/usr/local/etc/php/conf.d/php-phpmyadmin.ini` |

No restart policy.

### `redis`

| Item | Value |
|---|---|
| Image | `redis:latest` |
| Container name | `${COMPOSE_PROJECT_NAME}-redis` |
| Port | `127.0.0.1:${HOST_MACHINE_REDIS_PORT}:6379` |

No volume, command, password, or restart policy.

## Environment variables

`sample.env` is the committed set. `.env` is gitignored. The README says to copy `sample.env` to `.env`.

| Variable | Sample value | Consumed by |
|---|---|---|
| `COMPOSE_PROJECT_NAME` | `lamp` | Container names |
| `PHPVERSION` | `php56` | Web build context |
| `DOCUMENT_ROOT` | `./www` | Web volume. Compose also defaults this if unset |
| `APACHE_DOCUMENT_ROOT` | `/var/www/html` | Web environment. Compose defaults `/var/www/html` |
| `VHOSTS_DIR` | `./config/vhosts` | Web volume |
| `APACHE_LOG_DIR` | `./logs/apache2` | Not read by `docker-compose.yml`. Compose reads `LOG_DIR` |
| `PHP_INI` | `./config/php/php.ini` | Web volume and phpMyAdmin volume |
| `SSL_DIR` | `./config/ssl` | Web volume |
| `UPLOAD_LIMIT` | `512M` | phpMyAdmin |
| `MEMORY_LIMIT` | `512M` | phpMyAdmin |
| `XDEBUG_LOG_DIR` | `./logs/xdebug` | Web volume |
| `XDEBUG_PORT` | `9003` (`9000` commented) | `XDEBUG_CONFIG` on the web service |
| `DATABASE` | `mysql57` | Database build context |
| `MYSQL_INITDB_DIR` | `./config/initdb` | Database volume |
| `MYSQL_DATA_DIR` | `./data/mysql` | Database volume |
| `MYSQL_LOG_DIR` | `./logs/mysql` | Database volume |
| `HOST_MACHINE_UNSECURE_HOST_PORT` | `80` | Web port |
| `HOST_MACHINE_SECURE_HOST_PORT` | `443` | Web port |
| `HOST_MACHINE_MYSQL_PORT` | `3306` | Database port and web environment |
| `HOST_MACHINE_PMA_PORT` | `8080` | phpMyAdmin and web `PMA_PORT` |
| `HOST_MACHINE_PMA_SECURE_PORT` | `8443` | phpMyAdmin |
| `HOST_MACHINE_REDIS_PORT` | `6379` | Redis |
| `MYSQL_ROOT_PASSWORD` | `tiger` | Database, web, phpMyAdmin |
| `MYSQL_USER` | `docker` | Database, web, phpMyAdmin |
| `MYSQL_PASSWORD` | `docker` | Database, web, phpMyAdmin |
| `MYSQL_DATABASE` | `docker` | Database and web |

Commented, not applied: `MYSQL_USER='silicon'`, a password, `MYSQL_DATABASE='indawo_online'`. The password is omitted here on purpose.

`LOG_DIR` and `FileSyem` are compose variables with defaults and are not keys in `sample.env`.

## Build script

`dev_build_container.sh` is a bash script. It records `uname` and the path of `docker-compose`.

Database list: `mariadb103`, `mariadb104`, `mariadb105`, `mariadb106`, `mysql57`, `mysql8`.

For `-b` of `php54`, `php56`, `php71`, `php72`, or `php73`, it uses the first five entries (MariaDB variants and `mysql57`). For `php74`, `php8`, `php81`, `php82`, or `php83`, it uses the full list including `mysql8`. The usage string printed by the script omits `php83` even though the `elif` accepts it.

For each pair it copies `sample.env` into `./buildtest/<php>-<db>.env`, rewrites `COMPOSE_PROJECT_NAME` from `lamp` to `<php>-buildtest`, attempts to rewrite `PHPVERSION=php8` and `DATABASE=mysql8`, deletes `./data/mysql/*`, runs `docker-compose --env-file ... up -d --build`, sleeps 30 seconds, and curls the two test URLs. On failure it runs `down` and exits. On success it still runs `down`, then deletes the generated env files.

`sample.env` in this repo does not contain `PHPVERSION=php8` or `DATABASE=mysql8`, so those two `sed` expressions do not change the copy.

The script tells the operator it works on WSL2 except that WSL2 Windows-host mounts cannot be used for the data directory. Provenance: **Documented** in the script header.

## Git history of the stack

| Commit | Tree change that matters |
|---|---|
| `1d349a3` | `.gitignore` only. Header links to `https://www.atlassian.com/git/tutorials/saving-changes/gitignore` |
| `cc17467` | First compose file. Image `php:5.6-apache` (not a build), image `mysql:5.7`, ports `80:80` and `3306:3306`, volume `./src:/var/www/html`, volume `/external/drive:/external`, named volume `mysql_data`, database `indawo` |
| `2567dfc` | PHP service becomes a build of `Dockerfile-php`. Adds `MSSQL_*` environment placeholders. Dockerfile installs `unixodbc`, Microsoft `msodbcsql` (Debian 8 prod list, `ACCEPT_EULA=Y`), `pdo`, `pdo_mysql`, `mysqli`, `pdo_odbc`, and writes `/etc/odbc.ini` DSN `[MSSQL]` |
| `76c21e3` | Current tree. Deletes `Dockerfile-php`. Replaces README and compose. Adds `bin/`, `sample.env`, `LICENSE`, `todo.md`, `dev_build_container.sh`. Extends `.gitignore` |

Author emails: `jaco@overdrive.co.za` on `1d349a3`, `jaco@netstart.co.za` on `cc17467` and `2567dfc`, `ettiene@overdrive.co.za` on `76c21e3`.

## Local development

Documented start (current README):

```text
cp sample.env .env
docker compose up -d
```

Documented shell: `docker compose exec webserver bash`.

The February README used `docker-compose` and service name `php`. That service name is gone.

No `Makefile`, no Dev Container file, and no editor launch configuration are committed. `.vscode` is gitignored.

## Testing

No PHPUnit, no Compose healthcheck, and no CI config are in the tree.

The only automated check is `dev_build_container.sh`, and it depends on `test_db.php` and `test_db_pdo.php` being served at `http://localhost/`. Those files are not in git. `www/` is gitignored, which is where a document root would normally hold them.

This onboarding did not run Docker builds.

## Security notes present in the tree

- `sample.env` and the README publish the phpMyAdmin and MySQL development passwords (`tiger`, `docker` / `docker`). phpMyAdmin is configured as MySQL `root`.
- `sample.env` also contains a commented password for user `silicon`. It is not reproduced in Silicon Ark. It is still in the git object.
- `bin/php56/Dockerfile` places `phpinfo()` at the image document root.
- Xdebug 2 is installed with `xdebug.remote_enable=1`.
- Web HTTP, web HTTPS, and both phpMyAdmin ports are published without a `127.0.0.1` bind. MySQL and Redis are bound to loopback.
- PHP 5.6 and Debian Stretch are end-of-life bases. The Dockerfile opts into `archive.debian.org` so apt can still resolve.
- Image tags `phpmyadmin` (floating) and `redis:latest` are not digest-pinned. `php:5.6-apache` and `mysql:5.7` are floating minor tags of old majors.
- Composer is installed by piping the installer from `getcomposer.org` during build.
- The February commits stored development database passwords in `docker-compose.yml` and in the README. Those blobs remain in git history at `cc17467` and `2567dfc`.
- No application authentication is implemented here because no application is committed.

## Sonar and other scan-host files

Not in `76c21e3`: `CHANGELOG.md`, `docs/`, `sonar-project.properties`, `sonarqube_scan.bat`. `.gitignore` does not ignore those names. Contents: **Unknown / Requires Confirmation**.

## Related repositories

`indawo_online_docker` (`git@bitbucket.org:overdrive/indawo_online_docker.git`) has its own Silicon Ark set. Its `master` commit contains `.gitignore` only. This repo does not reference that remote. The commented database name `indawo_online` is the only shared token found.

`quantiviaLive` documents Indawo deploy strings (README title, `local.indawo`, CodeDeploy, FTP host `indawo.overdriveqa.co.za`). This repo does not mention those. A shared product is **Unknown / Requires Confirmation**.
