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
- Architecture Overview
- Prerequisites on Dokploy Server
- Step 1: Create Infrastructure Services
- Step 2: Deploy the Application
- Step 3: Configure Environment Variables
- Step 4: Configure Domain & HTTPS
- Step 5: Create the Database
- Step 6: Run Migrations & Seed
- Deploying Test/Beta Variants
- Troubleshooting
- 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
- In Dokploy UI → Create → Database → MySQL
- Set root password, default user, and password
- Dokploy will create a Swarm service (e.g.,
prodservers-prodsqlmain-kv3yph)
⚠️ IMPORTANT: The
DB_HOSTfor 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)
- In Dokploy UI → Create → Compose → new project for DragonflyDB
- 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
- Deploy the service
⚠️ IMPORTANT: The
REDIS_HOSTfor 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 lsordocker ps --format '{{.Names}}'.
Step 2: Deploy the Application
- In Dokploy UI → Create → Compose
- Connect to your Git repository (e.g.,
git@git.cr8.space:josh/BukidBountyApp.git) - Set branch to
main - The
docker-compose.ymlin 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 port9501. 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
- In Dokploy UI → your service → Domains tab
- Add domain:
- Host:
bukid.hesed.sbs(or your domain) - Container Port:
9501 - HTTPS: Enabled
- Certificate: Let's Encrypt
- Host:
- 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_DBnumbers (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
- In Dokploy UI → Create → Compose
- Connect to the same Git repo, optionally use a different branch (e.g.,
beta,develop) - Set environment variables with variant-specific values:
DB_DATABASE=bukid_beta
REDIS_DB=1
# DB_HOST and REDIS_HOST remain the same (same infrastructure)
- Configure domain in Domains tab:
beta.bukid.hesed.sbs→ port9501 - Deploy
- 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:
- Container health — Must show
(healthy), NOT(unhealthy)docker ps | grep bukidapp - If unhealthy, check app logs:
docker logs <container-name> --tail 100 - Most common cause: Redis or MySQL DNS resolution failure → wrong hostname in env vars
- Network: Verify container is on
dokploy-network:docker network inspect dokploy-network --format '{{range .Containers}}{{.Name}} {{end}}' | tr ' ' '\n' | grep bukid - 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_HOSTvalue - 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
.envvariables - 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