Add Docker and Coolify deployment setup

Add the container configuration, environment templates, and storage settings needed to deploy SPOTA with Docker and Coolify.
This commit is contained in:
Power BI Dev
2026-05-02 10:07:56 +07:00
commit 874dbbe8e8
12 changed files with 407 additions and 0 deletions

27
.dockerignore Normal file
View File

@@ -0,0 +1,27 @@
.git
.gitignore
spota_db/openfileslimit
spota_db
*.zip
*.rar
*.7z
*.tar
*.tar.gz
*.tgz
error_log
**/error_log
**/.DS_Store
index_gacor.php
index_gacor.html
cgi-bin
bekap
direktori-spn
pkm
rekomendasi-artikel
simpanghilir
sisesta
pedoman-old
files
files/**
!files/.gitkeep
img/curiga

14
.env.coolify.example Normal file
View File

@@ -0,0 +1,14 @@
APP_URL=https://spota.example.com
DB_USER=spota_user
DB_PASSWORD=change_this_database_password
DB_NAME=spota_spotadb
DB_SPOTA=spota_spotadb
DB_KONSULTASI=spota_konsultasi
DB_BIO=spota_spotadb
DB_DOSEN=spota_spotadb
SERVICE_DB_NAME=spota_spotadb
MYSQL_ROOT_PASSWORD=change_this_root_password
PHP_DISPLAY_ERRORS=0
FILES_STORAGE_PATH=/var/www/html/files

17
.env.example Normal file
View File

@@ -0,0 +1,17 @@
APP_PORT=8080
APP_URL=http://localhost:8080
DB_HOST=db
DB_PORT=3307
DB_USER=spota_user
DB_PASSWORD=spota_password
DB_NAME=spota_spotadb
DB_SPOTA=spota_spotadb
DB_KONSULTASI=spota_konsultasi
DB_BIO=spota_spotadb
DB_DOSEN=spota_spotadb
SERVICE_DB_NAME=spota_spotadb
MYSQL_ROOT_PASSWORD=root_password
PHP_DISPLAY_ERRORS=1
FILES_STORAGE_PATH=/var/www/html/files

62
.gitignore vendored Normal file
View File

@@ -0,0 +1,62 @@
# Environment and secrets
.env
.env.*
!.env.example
!.env.coolify.example
# Dependencies / generated packages
/vendor/
node_modules/
npm-debug.log*
yarn-error.log*
composer.lock
package-lock.json
# Runtime uploads and user-generated files
/files/
/files/**
!/files/.gitkeep
/img/curiga/
# Logs and temporary files
error_log
**/error_log
*.log
*.tmp
*.temp
tmp/
temp/
cache/
# Backups, archives, and exported bundles
*.zip
*.rar
*.7z
*.tar
*.tar.gz
*.tgz
*.bak
*.backup
*.old
bekap/
backup/
backups/
# Known unrelated/backup project folders
/direktori-spn/
/pkm/
/simpanghilir/
/sisesta/
/rekomendasi-artikel/
/pedoman-old/
# OS/editor files
.DS_Store
Thumbs.db
desktop.ini
.idea/
.vscode/
# Docker local data should be volumes, not Git content
docker-data/
.docker-data/

74
DOCKER.md Normal file
View File

@@ -0,0 +1,74 @@
# SPOTA Docker
Setup ini menjalankan aplikasi legacy SPOTA dengan PHP 7.4 Apache dan MySQL 8 dalam satu `docker-compose.yml`.
## Jalankan Lokal
```powershell
copy .env.example .env
docker compose up -d --build
```
Akses aplikasi:
```text
http://localhost:8080
http://localhost:8080/admin/login.php
http://localhost:8080/dosen/login.php
http://localhost:8080/mahasiswa/login.php
```
## Stop
```powershell
docker compose down
```
## Reset Database
Perintah ini menghapus volume DB dan import ulang dump dari `spota_db`.
```powershell
docker compose down -v
docker compose up -d
```
## Database
Dump diambil dari folder `spota_db` saat volume DB pertama kali dibuat.
Database yang di-import:
- `spota_spotadb`
- `spota_konsultasi`
- `spota_doxid`
- `spota_rek_artikel`
## Volume/Data
Folder berikut di-mount dari host supaya data upload tidak masuk image:
- `./files:/var/www/html/files`
- `./img:/var/www/html/img`
Opsional, upload berkas bisa dipindah ke lokasi di luar folder project dengan environment variable:
- `FILES_STORAGE_PATH=/absolute/path/to/files`
Jika `FILES_STORAGE_PATH` tidak diisi, aplikasi tetap memakai default `files/` di dalam document root.
Folder target akan dibuat otomatis saat upload pertama.
Jika path tersebut berada di luar `/var/www/html/files`, sesuaikan juga volume mount container ke lokasi yang sama.
Untuk Coolify, jadikan path ini persistent storage.
## Coolify
Sesuaikan environment berikut di Coolify:
- `APP_URL`
- `DB_USER`
- `DB_PASSWORD`
- `MYSQL_ROOT_PASSWORD`
- `PHP_DISPLAY_ERRORS=0` untuk production
Port internal app adalah `80`. Database internal memakai service name `db` pada port `3306`.

23
Dockerfile Normal file
View File

@@ -0,0 +1,23 @@
FROM php:7.4-apache
RUN apt-get update \
&& apt-get install -y --no-install-recommends \
libfreetype6-dev \
libjpeg62-turbo-dev \
libonig-dev \
libpng-dev \
libzip-dev \
unzip \
&& docker-php-ext-configure gd --with-freetype --with-jpeg \
&& docker-php-ext-install -j"$(nproc)" pdo_mysql mysqli gd zip mbstring exif \
&& a2enmod rewrite headers \
&& rm -rf /var/lib/apt/lists/*
COPY docker/php.ini /usr/local/etc/php/conf.d/spota.ini
COPY docker/apache-vhost.conf /etc/apache2/sites-available/000-default.conf
WORKDIR /var/www/html
COPY . /var/www/html
RUN mkdir -p /var/www/html/files /var/www/html/img /tmp/spota_sessions \
&& chown -R www-data:www-data /var/www/html/files /var/www/html/img /tmp/spota_sessions

31
Dockerfile.coolify Normal file
View File

@@ -0,0 +1,31 @@
FROM php:8.2-apache-bookworm
RUN rm -rf /var/lib/apt/lists/* \
&& apt-get clean \
&& apt-get update \
&& apt-get install -y --no-install-recommends \
libcurl4-openssl-dev \
libfreetype6-dev \
libjpeg62-turbo-dev \
libonig-dev \
libpng-dev \
libwebp-dev \
libzip-dev \
unzip \
&& docker-php-ext-configure gd --with-freetype --with-jpeg --with-webp \
&& docker-php-ext-install -j"$(nproc)" pdo_mysql mysqli gd curl zip mbstring exif \
&& a2enmod rewrite headers \
&& printf 'ServerName localhost\n' > /etc/apache2/conf-available/server-name.conf \
&& a2enconf server-name \
&& rm -rf /var/lib/apt/lists/*
COPY docker/php.ini /usr/local/etc/php/conf.d/spota.ini
COPY docker/apache-vhost.conf /etc/apache2/sites-available/000-default.conf
WORKDIR /var/www/html
COPY . /var/www/html/
RUN mkdir -p /var/www/html/files /var/www/html/img /tmp/spota_sessions \
&& chown -R www-data:www-data /var/www/html /tmp/spota_sessions
EXPOSE 80

View File

@@ -0,0 +1,49 @@
services:
app:
build:
context: .
dockerfile: Dockerfile.coolify
restart: unless-stopped
environment:
APP_URL: ${APP_URL}
DB_HOST: db
DB_USER: ${DB_USER}
DB_PASSWORD: ${DB_PASSWORD}
DB_NAME: ${DB_NAME:-spota_spotadb}
DB_SPOTA: ${DB_SPOTA:-spota_spotadb}
DB_KONSULTASI: ${DB_KONSULTASI:-spota_konsultasi}
DB_BIO: ${DB_BIO:-spota_spotadb}
DB_DOSEN: ${DB_DOSEN:-spota_spotadb}
SERVICE_DB_NAME: ${SERVICE_DB_NAME:-spota_spotadb}
PHP_DISPLAY_ERRORS: ${PHP_DISPLAY_ERRORS:-0}
FILES_STORAGE_PATH: ${FILES_STORAGE_PATH:-/var/www/html/files}
volumes:
- spota_files:/var/www/html/files
- spota_img:/var/www/html/img
depends_on:
db:
condition: service_healthy
db:
image: mysql:8.0
restart: unless-stopped
command: --default-authentication-plugin=mysql_native_password --character-set-server=latin1 --collation-server=latin1_swedish_ci --sql-mode=NO_ENGINE_SUBSTITUTION
environment:
MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
MYSQL_USER: ${DB_USER}
MYSQL_PASSWORD: ${DB_PASSWORD}
MYSQL_DATABASE: ${DB_NAME:-spota_spotadb}
volumes:
- spota_db_data:/var/lib/mysql
- ./docker/mysql-init:/docker-entrypoint-initdb.d:ro
- ./spota_db:/spota_db:ro
healthcheck:
test: ["CMD-SHELL", "mysqladmin ping -h localhost -uroot -p$${MYSQL_ROOT_PASSWORD}"]
interval: 10s
timeout: 5s
retries: 20
volumes:
spota_db_data:
spota_files:
spota_img:

51
docker-compose.yml Normal file
View File

@@ -0,0 +1,51 @@
services:
app:
build: .
container_name: spota-app
restart: unless-stopped
ports:
- "${APP_PORT:-8080}:80"
environment:
APP_URL: ${APP_URL:-http://localhost:8080}
DB_HOST: ${DB_HOST:-db}
DB_USER: ${DB_USER:-spota_user}
DB_PASSWORD: ${DB_PASSWORD:-spota_password}
DB_NAME: ${DB_NAME:-spota_spotadb}
DB_SPOTA: ${DB_SPOTA:-spota_spotadb}
DB_KONSULTASI: ${DB_KONSULTASI:-spota_konsultasi}
DB_BIO: ${DB_BIO:-spota_spotadb}
DB_DOSEN: ${DB_DOSEN:-spota_spotadb}
SERVICE_DB_NAME: ${SERVICE_DB_NAME:-spota_spotadb}
PHP_DISPLAY_ERRORS: ${PHP_DISPLAY_ERRORS:-1}
FILES_STORAGE_PATH: ${FILES_STORAGE_PATH:-/var/www/html/files}
volumes:
- ./files:/var/www/html/files
- ./img:/var/www/html/img
depends_on:
db:
condition: service_healthy
db:
image: mysql:8.0
container_name: spota-db
restart: unless-stopped
command: --default-authentication-plugin=mysql_native_password --character-set-server=latin1 --collation-server=latin1_swedish_ci
environment:
MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD:-root_password}
MYSQL_USER: ${DB_USER:-spota_user}
MYSQL_PASSWORD: ${DB_PASSWORD:-spota_password}
MYSQL_DATABASE: ${DB_NAME:-spota_spotadb}
ports:
- "${DB_PORT:-3307}:3306"
volumes:
- db_data:/var/lib/mysql
- ./docker/mysql-init:/docker-entrypoint-initdb.d:ro
- ./spota_db:/spota_db:ro
healthcheck:
test: ["CMD-SHELL", "mysqladmin ping -h localhost -uroot -p$${MYSQL_ROOT_PASSWORD}"]
interval: 10s
timeout: 5s
retries: 20
volumes:
db_data:

13
docker/apache-vhost.conf Normal file
View File

@@ -0,0 +1,13 @@
<VirtualHost *:80>
ServerName localhost
DocumentRoot /var/www/html
<Directory /var/www/html>
Options Indexes FollowSymLinks
AllowOverride All
Require all granted
</Directory>
ErrorLog ${APACHE_LOG_DIR}/error.log
CustomLog ${APACHE_LOG_DIR}/access.log combined
</VirtualHost>

View File

@@ -0,0 +1,33 @@
#!/bin/sh
set -e
mysql_exec() {
mysql -uroot -p"$MYSQL_ROOT_PASSWORD" "$@"
}
import_database() {
db_name="$1"
create_file="$2"
data_file="$3"
if [ -f "$create_file" ]; then
mysql_exec < "$create_file"
else
mysql_exec -e "CREATE DATABASE IF NOT EXISTS \`$db_name\`;"
fi
if [ -f "$data_file" ]; then
mysql_exec "$db_name" < "$data_file"
fi
}
import_database "spota_spotadb" "/spota_db/spota_spotadb.create" "/spota_db/spota_spotadb.sql"
import_database "spota_konsultasi" "/spota_db/spota_konsultasi.create" "/spota_db/spota_konsultasi.sql"
import_database "spota_doxid" "/spota_db/spota_doxid.create" "/spota_db/spota_doxid.sql"
import_database "spota_rek_artikel" "/spota_db/spota_rek_artikel.create" "/spota_db/spota_rek_artikel.sql"
mysql_exec -e "GRANT ALL PRIVILEGES ON \`spota_spotadb\`.* TO '$MYSQL_USER'@'%';"
mysql_exec -e "GRANT ALL PRIVILEGES ON \`spota_konsultasi\`.* TO '$MYSQL_USER'@'%';"
mysql_exec -e "GRANT ALL PRIVILEGES ON \`spota_doxid\`.* TO '$MYSQL_USER'@'%';"
mysql_exec -e "GRANT ALL PRIVILEGES ON \`spota_rek_artikel\`.* TO '$MYSQL_USER'@'%';"
mysql_exec -e "FLUSH PRIVILEGES;"

13
docker/php.ini Normal file
View File

@@ -0,0 +1,13 @@
display_errors = ${PHP_DISPLAY_ERRORS}
log_errors = On
error_reporting = E_ALL
max_execution_time = 120
max_input_time = 120
max_input_vars = 5000
memory_limit = 512M
post_max_size = 32M
upload_max_filesize = 32M
session.gc_maxlifetime = 604800
session.save_path = "/tmp/spota_sessions"
allow_url_fopen = On
date.timezone = Asia/Jakarta