Cerita dari Trial Deploy CodeIgniter 4 ke Hosting Tanpa SSH dan Sedikit Siasatnya

deploy codeigniter 4-hosting tanpa ssh

Ceritanya berawal dari hal yang nggak terlalu direncanakan. Suatu sore, iseng scroll Instagram, kelewat satu iklan hosting murah dari hostdata.id. Entah kenapa hari itu impulsnya kuat banget — nggak mikir panjang, langsung checkout. Modalnya? Nekat kasbon kantor duluan, mikir belakangan gimana caranya balikin.

Tapi ya, begitulah — setelah sekian lama cuma jadi wacana ("nanti kalau ada duit lebih, aku mau punya hosting sendiri buat naruh eksperimen-eksperimen kecil"), akhirnya kesampaian juga. Bukan buat proyek klien, bukan buat kerjaan kantor — murni buat tempat showcase apa yang udah saya pelajari dan kerjakan sendiri di luar jam kerja. Personal branding kecil-kecilan, biar portofolio nggak cuma jadi folder proyek yang numpuk di laptop doang.

Nah, punya hosting itu satu hal. Ternyata nge-live-in project beneran itu cerita lain — apalagi pas sadar kalau starter plan yang saya ambil itu nggak nyediain akses SSH sama sekali. Dan hampir semua dokumentasi resmi selalu nulis langkah-langkah dengan asumsi kamu punya akses shell. composer install di server? Tinggal SSH, ketik, selesai. Generate encryption key? SSH lagi. Lah kalau nggak ada?

Ini cerita saya nge-live-in inventori-pro, project CodeIgniter 4 yang biasa saya jalanin di local, ke hosting DirectAdmin dari hostdata.id itu. Nggak ada SSH. Cuma ada Git Manager, File Manager, dan Cron Jobs. Ternyata itu udah cukup — asal mau muter otak dikit.

Kenapa Nggak Langsung Push ke Server Aja?

Godaan pertama: kenapa nggak git remote add ke server, terus git push langsung? Ternyata DirectAdmin Git Manager punya pendekatan lebih rapi — dia yang narik dari GitHub, bukan nerima push dari local. Jadi cukup satu remote (origin, ke GitHub), dan server tinggal di-Fetch + Deploy kapan pun saya siap nge-live-kan sesuatu.

Ini juga jadi alasan saya akhirnya bikin alur kerja dua-branch: main buat kerja harian (lengkap dengan PR ala-ala di GitHub, biar ada jejak review meski solo project), dan production sebagai satu-satunya sumber yang boleh nyampe ke server.

Masalah #1: composer install Tanpa Terminal

CodeIgniter 4 (dan hampir semua framework modern) butuh vendor/ biar bisa jalan sama sekali. Folder itu sengaja di-gitignore — jadi nggak ikut ke-deploy dari GitHub. Biasanya solusinya gampang: SSH, composer install, selesai.

Solusi saya: cron job jadi terminal darurat. DirectAdmin nggak punya fitur "run once", tapi triknya sederhana — bikin cron job yang dijadwalin beberapa menit ke depan, biarin jalan sekali, terus hapus manual.

cd /home/USER/domains/NAMADOMAIN/public_html && \
/opt/alt/php83/usr/bin/php /usr/local/bin/composer install \
--no-dev --optimize-autoloader --no-interaction \
> /home/USER/composer_install.log 2>&1

Sempat gagal dua kali sebelum ini jalan mulus:

  • PHP version mismatchcomposer.lock saya nge-lock dependency yang butuh PHP 8.2+, sementara PHP CLI default cron masih 8.1. Solusinya manggil binary PHP secara eksplisit (/opt/alt/php83/...), bukan andelin default server.
  • Ekstensi phar disabled — ternyata di-nonaktifin default di setup PHP-nya. Tinggal diaktifin lewat menu "Select PHP Version" > Extensions.

Masalah #2: Struktur Folder CodeIgniter yang Bikin Bingung

Setelah deploy pertama, saya buka domainnya — muncul halaman placeholder default DirectAdmin, bukan aplikasi saya. Ternyata: CodeIgniter 4 punya web root di dalam folder public/, bukan di root project. Deploy tadi nge-checkout semuanya (app/, public/, vendor/) langsung ke public_html, jadi entry point-nya (public/index.php) ke-nested, sementara Apache tetep nyari index.html di level teratas.

Solusinya: pindahin isi public/ naik satu level, terus edit satu baris referensi path di index.php:

// Sebelum (asumsi index.php ada di dalam folder public/)
require FCPATH . '../app/Config/Paths.php';

// Sesudah (index.php sekarang sejajar dengan app/)
require FCPATH . 'app/Config/Paths.php';

Masalah #3: Config yang "Kebawa" dari Local

Ini yang paling berkesan buat saya. Pas ngecek git status, ternyata app/Config/Database.php saya statusnya modified — isinya path socket MariaDB dari setup local (MacPorts) yang saya hardcode langsung di file itu. Kalau ini ke-deploy apa adanya, server bakal nyoba connect ke socket yang jelas nggak ada di sana.

Untungnya CodeIgniter 4 udah nyediain solusi resmi: baca konfigurasi dari .env, bukan hardcode di PHP. Dan buat koneksi lewat filesystem socket secara spesifik, path-nya ditaruh di setting hostname, bukan bikin key custom sendiri:

database.default.hostname = /opt/local/var/run/mariadb-10.11/mysqld.sock
database.default.database = inventori_pro
database.default.username = root
database.default.password = root
database.default.DBDriver = MySQLi

Database.php di-revert ke versi generic, dan sekarang aman di-deploy ke environment manapun — local maupun production baca config masing-masing dari .env sendiri-sendiri (yang tentunya di-gitignore).

Masalah #4: Nemu Bug Security Nggak Sengaja

Pas lagi beresin login, ketemu sesuatu yang bikin saya diem sebentar: password disimpan pakai hash('sha256', $password). Sekilas kelihatan "aman" karena di-hash, tapi sha256 didesain buat kecepatan (cocok buat checksum file), bukan buat password — gampang di-brute-force, dan nggak ada salt sama sekali.

Perbaikannya ganti ke password_hash() / password_verify() bawaan PHP, yang otomatis nyertain salt random tiap kali dan sengaja dibikin lambat:

// Sebelum
$hash = hash('sha256', $password);
if ($user && $user['password'] === $hash) { ... }

// Sesudah
if ($user && password_verify($password, $user['password'])) { ... }

Sekalian nambahin fitur "Ganti Password" self-service, biar nggak ada lagi cerita password default 12345 nempel selamanya.

Insiden Kecil yang Bikin Ngakak

Dua kejadian receh yang layak dicatat biar nggak keulang:

  1. Update password production pakai hash yang salah — karena sempat testing fitur "Ganti Password" duluan di local sebelum deploy, hash yang saya UPDATE ke production ternyata hash versi sebelum testing, bukan yang aktif sekarang. Admin production langsung ke-lock out dari password yang bahkan saya sendiri udah lupa.
  2. Deploy nggak nge-refresh apa-apa — ternyata DA Git Manager itu dua tahap: Fetch (narik commit terbaru ke bare repo) dan Deploy (checkout ke public_html). Klik Deploy tanpa Fetch dulu artinya checkout dari commit lama. Sepele, tapi bikin bingung setengah jam sebelum ketauan.

Pelajaran yang Saya Bawa Pulang

  • Nggak ada SSH bukan berarti nggak ada jalan. Cron job bisa jadi terminal darurat buat one-off command, asal disiplin hapus lagi setelah selesai.
  • .gitignore yang berantakan (conflict marker belum di-resolve, duplikasi) itu bukan cuma soal kerapian — bisa nyembunyiin masalah lebih besar yang baru ketauan pas udah kepepet.
  • Config yang beda per environment harus lewat .env, titik. Sekali hardcode "buat sementara doang", biasanya lupa dibenerin sampai kebawa ke production.
  • Baca kode lama dengan mata baru itu berharga. Bug sha256 udah ada dari awal, tapi baru ketauan pas saya iseng liat isi controller buat keperluan lain sama sekali.

Semua proses ini kelar tanpa satu baris pun perintah SSH — cuma modal Git Manager, File Manager, Cron Jobs, dan kesabaran buat ngulang cron test berkali-kali. Kalau kamu lagi di posisi yang sama (starter hosting, nggak ada shell access, tapi punya fitur Git), semoga cerita ini nunjukin: keterbatasan itu nggak selalu berarti jalan buntu.

Tag: #CodeIgniter, #PHP, #Web Development, #DirectAdmin, #Deployment, #Belajar Ngoding

 

Tidak ada komentar