Files
BarangaySystem/dokploy-deployment-guide.md
2026-06-06 18:43:00 +08:00

14 KiB

Dokploy Deployment Guide — BukidBountyApp (Hypervel/Swoole)

This guide documents the exact process for deploying BukidBountyApp (and derivative projects) on a Dokploy-managed server. It is based on real troubleshooting and verified configuration.


Table of Contents

  1. Architecture Overview
  2. Prerequisites on Dokploy Server
  3. Step 1: Create Infrastructure Services
  4. Step 2: Deploy the Application
  5. Step 3: Configure Environment Variables
  6. Step 4: Configure Domain & HTTPS
  7. Step 5: Create the Database
  8. Step 6: Run Migrations & Seed
  9. Deploying Test/Beta Variants
  10. Troubleshooting
  11. Reference: Working Configuration Files

Architecture Overview

Internet → Traefik (ports 80/443) → dokploy-network → bukidapp container (port 9501)
                                                     → DragonflyDB (port 6379)
                                                     → MySQL (port 3306)
Component Type Network
BukidBountyApp Docker Compose (Dokploy) dokploy-network
MySQL 8 Dokploy Database (Swarm) dokploy-network
DragonflyDB Docker Compose (Dokploy) dokploy-network
Traefik Managed by Dokploy dokploy-network

Prerequisites on Dokploy Server

  • Dokploy installed and running
  • Traefik configured with Let's Encrypt cert resolver
  • DNS pointing your domain to the server IP

Step 1: Create Infrastructure Services

1a. Create MySQL Database

  1. In Dokploy UI → CreateDatabaseMySQL
  2. Set root password, default user, and password
  3. Dokploy will create a Swarm service (e.g., prodservers-prodsqlmain-kv3yph)

⚠️ IMPORTANT: The DB_HOST for your app is the Swarm service name, not the container name. For Swarm services, use the service name directly (e.g., prodservers-prodsqlmain-kv3yph).

Find the MySQL service name (run on Hostinger server terminal):

docker service ls | grep -i sql

1b. Create DragonflyDB (Redis Alternative)

  1. In Dokploy UI → CreateCompose → new project for DragonflyDB
  2. Use this docker-compose.yml:
version: '3.8'
services:
  dragonflydb:
    image: 'docker.dragonflydb.io/dragonflydb/dragonfly'
    ulimits:
      memlock: -1
    ports:
      - "6379:6379"
    volumes:
      - dragonflydata:/data
    environment: 
      - DFLY_requirepass
    networks:
      - dokploy-network

volumes:
  dragonflydata:

networks:
  dokploy-network:
    external: true
  1. Deploy the service

⚠️ IMPORTANT: The REDIS_HOST for your app is the full container name, not the Dokploy project name. For Docker Compose services, the container name follows the pattern: {project-name}-{service-name}-{replica}.

Find the DragonflyDB container name (run on Hostinger server terminal):

docker ps --format '{{.Names}}' | grep dragonfly
# Example output: prodservers-dragonflydb-rnfaje-dragonflydb-1

🔴 CRITICAL DISTINCTION:

  • Swarm services (Dokploy Databases) → use the service name as hostname
  • Compose services (like DragonflyDB) → use the container name as hostname

These are different! Always verify with docker service ls or docker ps --format '{{.Names}}'.


Step 2: Deploy the Application

  1. In Dokploy UI → CreateCompose
  2. Connect to your Git repository (e.g., git@git.cr8.space:josh/BukidBountyApp.git)
  3. Set branch to main
  4. The docker-compose.yml in the repo will be used automatically

Working docker-compose.yml

services:
  bukidapp:
    build:
      context: .
      dockerfile: Dockerfile.php
    ports:
      - 9501
    networks:
      - dokploy-network
    healthcheck:
      test: [ "CMD-SHELL", "curl -f http://localhost:9501/health || exit 1" ]
      interval: 10s
      timeout: 5s
      retries: 5
      start_period: 30s

networks:
  dokploy-network:
    external: true

🔴 CAUTION: Do NOT use host port mapping like 9522:9501. Just expose the internal port 9501. Traefik handles external routing via labels that Dokploy injects automatically.


Step 3: Configure Environment Variables

In Dokploy UI → your service → Environment tab, set:

# === Database (MySQL) ===
DB_CONNECTION=mysql
DB_HOST=<mysql-swarm-service-name>
DB_DATABASE=bukid
DB_PORT=3306
DB_USERNAME=mysql
DB_PASSWORD=<your-mysql-password>

# === Cache/Redis (DragonflyDB) ===
CACHE_DRIVER=redis
REDIS_HOST=<dragonflydb-container-name>
REDIS_AUTH=<your-dragonfly-password>
REDIS_PORT=6379
REDIS_DB=0

How to Find the Correct Hostnames

Service Type How to Find Hostname Example
MySQL Swarm docker service ls | grep sql prodservers-prodsqlmain-kv3yph
DragonflyDB Compose docker ps --format '{{.Names}}' | grep dragonfly prodservers-dragonflydb-rnfaje-dragonflydb-1
PostgreSQL Swarm docker service ls | grep pgsql prodservers-prodpgsqlmain-x8frsw

💡 TIP: You can verify connectivity from the app container using Dokploy's container terminal (UI):

ping -c 2 <hostname>

Step 4: Configure Domain & HTTPS

  1. In Dokploy UI → your service → Domains tab
  2. Add domain:
    • Host: bukid.hesed.sbs (or your domain)
    • Container Port: 9501
    • HTTPS: Enabled
    • Certificate: Let's Encrypt
  3. Deploy/Redeploy the service

What Dokploy Adds Automatically

Dokploy injects these Traefik labels into your compose at deploy time:

labels:
  - traefik.docker.network=dokploy-network
  - traefik.enable=true
  - traefik.http.routers.<id>-web.rule=Host(`bukid.hesed.sbs`)
  - traefik.http.routers.<id>-web.entrypoints=web
  - traefik.http.services.<id>-web.loadbalancer.server.port=9501
  - traefik.http.routers.<id>-web.middlewares=redirect-to-https@file
  - traefik.http.routers.<id>-websecure.rule=Host(`bukid.hesed.sbs`)
  - traefik.http.routers.<id>-websecure.entrypoints=websecure
  - traefik.http.services.<id>-websecure.loadbalancer.server.port=9501
  - traefik.http.routers.<id>-websecure.tls.certresolver=letsencrypt

⚠️ IMPORTANT: You do NOT need to add these labels manually in your docker-compose.yml. Dokploy handles them when you configure the domain in the UI. Adding them manually can cause label conflicts.


Step 5: Create the Database

The MySQL database must be created manually after the MySQL service is running.

Via Hostinger server terminal:

# Connect to MySQL container
docker exec -it $(docker ps --format '{{.Names}}' | grep prodsqlmain) mysql -u root -p

Inside MySQL shell:

CREATE DATABASE bukid;
GRANT ALL PRIVILEGES ON bukid.* TO 'mysql'@'%';
FLUSH PRIVILEGES;
EXIT;

Step 6: Run Migrations & Seed

Via Dokploy UI → your service → Terminal:

php artisan migrate
php artisan db:seed

Or from the Hostinger server terminal:

docker exec <bukidapp-container-name> php artisan migrate
docker exec <bukidapp-container-name> php artisan db:seed

Deploying Test/Beta Variants

To deploy a test or beta version of the same app with separate databases:

1. Plan Variant Resources

Variant MySQL DB Name Redis DB Domain
Prod bukid 0 bukid.hesed.sbs
Beta bukid_beta 1 beta.bukid.hesed.sbs
Test bukid_test 2 test.bukid.hesed.sbs

💡 TIP: You can reuse the same MySQL server and same DragonflyDB instance for all variants. Just create different databases in MySQL and use different REDIS_DB numbers (0-15).

2. Create Additional MySQL Databases

Via Hostinger server terminal:

docker exec -it $(docker ps --format '{{.Names}}' | grep prodsqlmain) mysql -u root -p
CREATE DATABASE bukid_beta;
CREATE DATABASE bukid_test;
GRANT ALL PRIVILEGES ON bukid_beta.* TO 'mysql'@'%';
GRANT ALL PRIVILEGES ON bukid_test.* TO 'mysql'@'%';
FLUSH PRIVILEGES;
EXIT;

3. Deploy New Compose Project in Dokploy

  1. In Dokploy UI → CreateCompose
  2. Connect to the same Git repo, optionally use a different branch (e.g., beta, develop)
  3. Set environment variables with variant-specific values:
DB_DATABASE=bukid_beta
REDIS_DB=1
# DB_HOST and REDIS_HOST remain the same (same infrastructure)
  1. Configure domain in Domains tab: beta.bukid.hesed.sbs → port 9501
  2. Deploy
  3. Run migrations via Dokploy container terminal:
    php artisan migrate
    php artisan db:seed
    

4. Verify Each Variant

Via Hostinger server terminal:

# Find all bukid containers
docker ps --format '{{.Names}}' | grep bukid

# Check health of each — must show (healthy)
docker ps | grep bukid

Troubleshooting

404 Not Found (Domain Access)

Check this in order:

  1. Container health — Must show (healthy), NOT (unhealthy)
    docker ps | grep bukidapp
    
  2. If unhealthy, check app logs:
    docker logs <container-name> --tail 100
    
  3. Most common cause: Redis or MySQL DNS resolution failure → wrong hostname in env vars
  4. Network: Verify container is on dokploy-network:
    docker network inspect dokploy-network --format '{{range .Containers}}{{.Name}} {{end}}' | tr ' ' '\n' | grep bukid
    
  5. Redeploy: Domain/label changes require a full Redeploy in Dokploy UI, not just restart

Redis DNS Lookup Failed

RedisException: DNS Lookup resolve failed
  • Cause: Wrong REDIS_HOST value
  • Fix: Use full container name → docker ps --format '{{.Names}}' | grep dragonfly

MySQL Access Denied

SQLSTATE[HY000] [1044] Access denied for user 'mysql'@'%' to database 'bukid'
  • Cause: Database doesn't exist or user lacks permissions
  • Fix: Create the database and grant permissions (see Step 5)

Traefik Logs Empty

docker logs dokploy-traefik --tail 50

If empty, Traefik hasn't encountered errors — the issue is likely the container being unhealthy.

Container Keeps Restarting

Check logs for startup errors:

docker logs <container-name> --tail 200

Common causes:

  • Missing .env variables
  • Database not reachable
  • Redis not reachable
  • PHP extension missing

Reference: Working Configuration Files

docker-compose.yml (committed in repo root)

services:
  bukidapp:
    build:
      context: .
      dockerfile: Dockerfile.php
    ports:
      - 9501
    networks:
      - dokploy-network
    healthcheck:
      test: [ "CMD-SHELL", "curl -f http://localhost:9501/health || exit 1" ]
      interval: 10s
      timeout: 5s
      retries: 5
      start_period: 30s

networks:
  dokploy-network:
    external: true

Dockerfile.php

FROM hyperf/hyperf:8.3-alpine-v3.19-swoole-v6

USER root

RUN apk add --no-cache \
    postgresql-dev \
    php83-pdo_pgsql \
    php83-pgsql \
    7zip

RUN apk add --no-cache curl \
    && curl -fsSL https://unofficial-builds.nodejs.org/download/release/v20.19.0/node-v20.19.0-linux-x64-musl.tar.gz \
    | tar -xz -C /usr/local --strip-components=1 \
    && node -v \
    && npm -v

RUN echo "extension=pdo_pgsql.so" > /etc/php83/conf.d/01_pdo_pgsql.ini && \
    echo "extension=pgsql.so" > /etc/php83/conf.d/00_pgsql.ini

WORKDIR /var/app

COPY package*.json ./
RUN npm install

COPY . .
RUN npm run build

EXPOSE 9501

COPY composer.json composer.lock ./
RUN composer install --no-dev --optimize-autoloader

CMD ["php", "artisan", "serve"]

Required Environment Variables

# Database
DB_CONNECTION=mysql
DB_HOST=<mysql-swarm-service-name>
DB_DATABASE=bukid
DB_PORT=3306
DB_USERNAME=mysql
DB_PASSWORD=<password>

# Redis / DragonflyDB
CACHE_DRIVER=redis
REDIS_HOST=<dragonflydb-container-name>
REDIS_AUTH=<password>
REDIS_PORT=6379
REDIS_DB=0

/health Route (in routes/web.php)

Route::get('/health', function () {
    return 'OK';
});

Quick Reference: Useful Commands

All commands are run on the Hostinger server terminal unless otherwise noted.

# List all running containers
docker ps

# Find a specific container name
docker ps --format '{{.Names}}' | grep <keyword>

# List Swarm services (for Dokploy Databases)
docker service ls

# Check container health
docker ps | grep <container>

# View container logs
docker logs <container-name> --tail 100

# Inspect dokploy-network's connected containers
docker network inspect dokploy-network --format '{{range .Containers}}{{.Name}} {{end}}'

# Test connectivity (run in Dokploy container terminal via UI)
ping -c 2 <target-hostname>

# Run artisan commands (via Dokploy container terminal via UI)
php artisan migrate
php artisan db:seed

# Connect to MySQL (via Hostinger server terminal)
docker exec -it $(docker ps --format '{{.Names}}' | grep prodsqlmain) mysql -u root -p