Files
Aplikasi-Kehadiran-KTM/DOCKER.md
Saufi dfd22ff2af Aplikasi kehadiran KTM ambil peti undi (SPR 2026)
Sistem untuk admin DUN merekod waktu KTM (Ketua Tempat Mengundi) hadir
mengambil peti undi. Data KTM dibaca daripada DB sppm (spr2026) melalui
connection kedua — hanya penempatan berjawatan KTM yang dipaparkan.

- Dashboard: senarai KTM ikut pusat mengundi (asc) & saluran (asc),
  status BELUM HADIR (merah) / HADIR (hijau) + tarikh & masa
- Carian nama/no. KP, tapisan pusat mengundi & status
- Tanda hadir / batal dengan kawalan DUN (admin terhad ke DUN sendiri)
- Eksport Excel (worksheet Hadir & Belum Hadir) guna SimpleXlsx
- Peranan: superadmin (urus pengguna, semua DUN) & admin DUN
- Docker: nginx + php-fpm + queue + scheduler + webhook deploy,
  port 127.0.0.1:8009, TZ Asia/Kuala_Lumpur di semua container
- Zon waktu aplikasi: Asia/Kuala_Lumpur (APP_TIMEZONE)

Berasaskan scaffold spr2026_taklimat.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-11 02:40:00 +08:00

5.0 KiB

Running with Docker

This app is packaged as a small Docker stack and is designed to run behind the host's existing Nginx, connecting to the host's existing MySQL (where both sppm_ktm and the external sppm database live).

Internet
   │
   ▼
host nginx  (ktmspr.apps.mbip.my, port 80)   ← reverse proxy
   │  proxy_pass 127.0.0.1:8009
   ▼
web container (nginx)  ──fastcgi──►  app container (php-fpm 8.4)
                                          │
                                          ▼  host.docker.internal:3306
                                     host MySQL (sppm_ktm, sppm)

Containers:

Service Image Role
web ktmspr-web nginx, serves static assets, proxies PHP. Binds 127.0.0.1:8009.
app ktmspr-app PHP 8.4-FPM. Runs migrations on boot.
queue ktmspr-app queue:work (database queue).
scheduler ktmspr-app schedule:work (Laravel scheduler).

Prerequisites on the host (Ubuntu 24.04)

  • Docker Engine + Compose plugin:
    sudo apt-get update
    sudo apt-get install -y docker.io docker-compose-v2
    sudo systemctl enable --now docker
    
  • The existing Nginx and MySQL services already installed on the host.

1. Configure host MySQL to accept connections from containers

On Linux, host.docker.internal resolves to the Docker bridge gateway (e.g. 172.17.0.1) via the host-gateway mapping already set in docker-compose.yml. The host MySQL must accept connections on that interface.

a) Listen on all interfaces — edit /etc/mysql/mysql.conf.d/mysqld.cnf:

bind-address = 0.0.0.0

Then restart: sudo systemctl restart mysql.

b) Allow the app user from the Docker subnet. Prefer a dedicated user over root (and then set the credentials in .env.docker):

CREATE USER 'ktm'@'172.%' IDENTIFIED BY 'CHANGE_ME_STRONG';
GRANT ALL PRIVILEGES ON sppm_ktm.* TO 'ktm'@'172.%';
GRANT SELECT          ON sppm.*          TO 'ktm'@'172.%';
FLUSH PRIVILEGES;

Make sure the sppm_ktm database exists:

CREATE DATABASE IF NOT EXISTS sppm_ktm
  CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

c) Firewall — if ufw is active, allow MySQL from the Docker subnet only:

sudo ufw allow from 172.16.0.0/12 to any port 3306 proto tcp

2. Set the container environment

.env.docker holds the runtime config (it is git-ignored because it contains credentials). Review it and update at least:

  • DB_USERNAME / DB_PASSWORD and SPPM_DB_* to match step 1b.
  • APP_KEY is pre-filled; to rotate it run docker compose run --rm app php artisan key:generate --show and paste the result.

DB_HOST / SPPM_DB_HOST are already set to host.docker.internal — leave them.


3. Build and start

docker compose build
docker compose up -d

On boot the app container waits for MySQL, caches config/routes/views, runs php artisan migrate --force, and starts php-fpm. Check it:

docker compose ps
docker compose logs -f app
curl -I http://127.0.0.1:8009        # should return HTTP 200/302

4. Point host Nginx at the stack

sudo cp docker/host-nginx/ktmspr.apps.mbip.my.conf \
        /etc/nginx/sites-available/ktmspr.apps.mbip.my
sudo ln -s /etc/nginx/sites-available/ktmspr.apps.mbip.my \
           /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx

The app should now be reachable at http://ktmspr.apps.mbip.my.


Common operations

# Tail logs
docker compose logs -f app web queue

# Artisan / composer inside the app container
docker compose exec app php artisan about
docker compose exec app php artisan tinker

# Re-cache config after editing .env.docker
docker compose exec app php artisan config:cache

# Rebuild & redeploy after pulling new code
docker compose build && docker compose up -d

Notes & limitations

  • Static uploads: web (nginx) serves a baked copy of public/. Files written at runtime to storage/app/public (the public disk + storage:link) live only in the app container and are not served by nginx. The current app renders QR codes as inline SVG, so this isn't needed today — but if you later serve uploaded files via /storage, switch web to share the app's public/storage via a volume.
  • Persistent data: the named volume app_storage keeps logs, compiled views, and storage/app across restarts. Application/session data lives in MySQL.
  • TLS: terminate HTTPS at the host Nginx (e.g. with certbot) if/when the domain moves to https://. Then add SESSION_SECURE_COOKIE=true and APP_URL=https://... in .env.docker.
  • HEALTHCHECK / scheduler: there are currently no scheduled commands, but the scheduler service is included so future app/Console schedules work without changing the stack. Remove it if you prefer.