Various Yii 3.0 related documentation
This guide walks you through deploying a Yii application to Docker Swarm from a blank server, using Caddy as a reverse proxy and a container registry (Forgejo or Gitea).
graph LR
A[Internet] --> B[Reverse Proxy: Caddy or Traefik]
B --> C[app1.example.com]
B --> D[app2.example.com]
subgraph Docker Swarm Cluster
B
C
D
end
For installation instructions, see the official Docker documentation.
Initialize your server as a Docker Swarm manager:
docker swarm init --advertise-addr <YOUR_SERVER_IP>
Replace <YOUR_SERVER_IP>
with your server’s public IP address.
Create a dedicated overlay network for Caddy to communicate with your services:
docker network create --driver=overlay caddy_public
You need a container registry to store your Docker images. Choose one of the following options.
Deploy Forgejo as a container registry.
Create a file forgejo-stack.yml
:
version: '3.8'
services:
forgejo:
image: codeberg.org/forgejo/forgejo:1.21
ports:
- "3000:3000"
volumes:
- forgejo_data:/data
networks:
- caddy_public
deploy:
labels:
- "caddy=git.example.com"
- "caddy.reverse_proxy="
volumes:
forgejo_data:
networks:
caddy_public:
external: true
Deploy Forgejo:
docker stack deploy -c forgejo-stack.yml forgejo
Replace git.example.com
with your desired subdomain.
After deployment, access Forgejo at https://git.example.com
and complete the initial setup. Make sure to enable the container registry in the settings.
Deploy Gitea as a container registry.
Create a file gitea-stack.yml
:
version: '3.8'
services:
gitea:
image: gitea/gitea:latest
ports:
- "3000:3000"
volumes:
- gitea_data:/data
networks:
- caddy_public
deploy:
labels:
- "caddy=git.example.com"
- "caddy.reverse_proxy="
volumes:
gitea_data:
networks:
caddy_public:
external: true
Deploy Gitea:
docker stack deploy -c gitea-stack.yml gitea
Replace git.example.com
with your desired subdomain.
After deployment, access Gitea at https://git.example.com
and complete the initial setup. Make sure to enable the container registry in the settings.
The Yii application template includes Caddy labels by default. Deploy Caddy with automatic HTTPS.
Create a file caddy-stack.yml
:
version: '3.8'
services:
caddy:
image: lucaslorentz/caddy-docker-proxy:ci-alpine
ports:
- "80:80"
- "443:443"
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- caddy_data:/data
networks:
- caddy_public
volumes:
caddy_data:
networks:
caddy_public:
external: true
Deploy Caddy:
docker stack deploy -c caddy-stack.yml caddy
Caddy automatically discovers services with Caddy labels and sets up HTTPS using Let’s Encrypt.
[!IMPORTANT] Make sure your domain DNS records are configured and pointing to your server before deploying services with Caddy labels, as Let’s Encrypt requires domain validation.
If you prefer Traefik over Caddy, deploy it as follows:
Create a file traefik-stack.yml
:
version: '3.8'
services:
traefik:
image: traefik:v2.10
command:
- "--api.insecure=true"
- "--providers.docker=true"
- "--providers.docker.swarmMode=true"
- "--providers.docker.exposedbydefault=false"
- "--entrypoints.web.address=:80"
- "--entrypoints.websecure.address=:443"
- "--certificatesresolvers.letsencrypt.acme.tlschallenge=true"
- "--certificatesresolvers.letsencrypt.acme.email=admin@example.com"
- "--certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json"
ports:
- "80:80"
- "443:443"
- "8080:8080"
volumes:
- "/var/run/docker.sock:/var/run/docker.sock:ro"
- "traefik_certificates:/letsencrypt"
networks:
- caddy_public
deploy:
placement:
constraints:
- node.role == manager
volumes:
traefik_certificates:
networks:
caddy_public:
external: true
Deploy Traefik:
docker stack deploy -c traefik-stack.yml traefik
If using Traefik, you’ll need to modify the Yii application’s docker/prod/compose.yml
to use Traefik labels instead of Caddy labels.
Example Traefik labels for your application:
deploy:
labels:
- "traefik.enable=true"
- "traefik.http.routers.app.rule=Host(`app.example.com`)"
- "traefik.http.routers.app.entrypoints=websecure"
- "traefik.http.routers.app.tls.certresolver=letsencrypt"
- "traefik.http.services.app.loadbalancer.server.port=80"
The Yii application template includes a Makefile with deployment commands. Update the docker/.env
file in your project:
STACK_NAME=myapp
#
# Production
#
PROD_HOST=app.example.com
PROD_SSH="ssh://user@your-server-ip"
IMAGE=git.example.com/username/myapp
IMAGE_TAG=latest
Replace the values:
STACK_NAME
: A unique name for your application stackPROD_HOST
: The domain name where your app will be accessiblePROD_SSH
: SSH connection string to your server (format: ssh://user@host
)IMAGE
: Full path to your container image in the registryIMAGE_TAG
: Image tag, typically latest
or a version numberUpdate docker/prod/.env
with your production environment variables:
APP_ENV=prod
YII_DEBUG=false
YII_ENV=prod
# Database configuration
DB_HOST=db
DB_NAME=myapp
DB_USER=myapp
DB_PASSWORD=secure_password_here
# Add other environment-specific variables
[!WARNING] Never commit sensitive credentials to version control. Use
docker/prod/override.env
for sensitive values and add it to.gitignore
.
The default docker/prod/compose.yml
includes:
services:
app:
image: ${IMAGE}:${IMAGE_TAG}
networks:
- caddy_public
volumes:
- runtime:/app/runtime
- caddy_data:/data
- caddy_config:/config
env_file:
- path: ./prod/.env
- path: ./prod/override.env
required: false
deploy:
replicas: 2
update_config:
delay: 10s
parallelism: 1
order: start-first
failure_action: rollback
monitor: 10s
rollback_config:
parallelism: 0
order: stop-first
restart_policy:
condition: on-failure
delay: 5s
max_attempts: 3
window: 120s
labels:
caddy: ${PROD_HOST:-app.example.com}
caddy.reverse_proxy: ""
This configuration:
If you need a database, add it to the stack:
services:
app:
# ... existing configuration ...
db:
image: postgres:15-alpine
environment:
POSTGRES_DB: myapp
POSTGRES_USER: myapp
POSTGRES_PASSWORD_FILE: /run/secrets/db_password
volumes:
- db_data:/var/lib/postgresql/data
networks:
- caddy_public
deploy:
placement:
constraints:
- node.role == manager
secrets:
- db_password
volumes:
runtime:
db_data:
secrets:
db_password:
external: true
Create the database password secret on the server:
echo "your_secure_password" | docker secret create db_password -
Configure Docker to authenticate with your container registry:
docker login git.example.com
Enter your username and password when prompted.
Use the Makefile to build your production image:
make prod-build
This runs the command defined in the Makefile:
docker build --file docker/Dockerfile --target prod --pull -t ${IMAGE}:${IMAGE_TAG} .
The Dockerfile uses a multi-stage build:
www-data
)Push your built image to the container registry:
make prod-push
This executes:
docker push ${IMAGE}:${IMAGE_TAG}
Set up SSH key-based authentication to your server:
# Generate SSH key (if you don't have one)
ssh-keygen -t ed25519 -C "your_email@example.com"
# Copy the key to your server
ssh-copy-id user@your-server-ip
# Add the SSH host to your SSH config (~/.ssh/config)
cat >> ~/.ssh/config << EOF
Host docker-web
HostName your-server-ip
User user
IdentityFile ~/.ssh/id_ed25519
EOF
Create a Docker context for remote deployment:
docker context create swarm-prod --docker "host=ssh://docker-web"
Alternatively, configure the DOCKER_HOST
environment variable:
export DOCKER_HOST=ssh://docker-web
Deploy your application stack to Docker Swarm:
make prod-deploy
This executes:
docker -H ${PROD_SSH} stack deploy --with-registry-auth -c docker/compose.yml -c docker/prod/compose.yml ${STACK_NAME}
The --with-registry-auth
flag ensures the Swarm nodes can pull images from your private registry.
Check the status of your services:
docker -H ssh://docker-web service ls
docker -H ssh://docker-web service ps ${STACK_NAME}_app
View logs:
docker -H ssh://docker-web service logs ${STACK_NAME}_app
# View all logs
docker -H ssh://docker-web service logs -f ${STACK_NAME}_app
# View logs from the last 100 lines
docker -H ssh://docker-web service logs --tail 100 ${STACK_NAME}_app
# View logs with timestamps
docker -H ssh://docker-web service logs -t ${STACK_NAME}_app
Adjust the number of replicas:
docker -H ssh://docker-web service scale ${STACK_NAME}_app=3
Or update the replicas
value in docker/prod/compose.yml
and redeploy.
Add resource limits to prevent containers from consuming all server resources. Update docker/prod/compose.yml
:
services:
app:
# ... existing configuration ...
deploy:
resources:
limits:
cpus: '0.5'
memory: 512M
reservations:
cpus: '0.25'
memory: 256M
Instead of environment variables, use Docker secrets for sensitive information:
# Create secrets
echo "database_password" | docker secret create db_password -
echo "api_key" | docker secret create api_key -
Update docker/prod/compose.yml
:
services:
app:
secrets:
- db_password
- api_key
secrets:
db_password:
external: true
api_key:
external: true
Access secrets in your application at /run/secrets/secret_name
.
Configure UFW (Uncomplicated Firewall) on your server:
# Allow SSH
sudo ufw allow 22/tcp
# Allow HTTP and HTTPS
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
# Allow Docker Swarm ports (if you plan to add more nodes)
sudo ufw allow 2377/tcp
sudo ufw allow 7946/tcp
sudo ufw allow 7946/udp
sudo ufw allow 4789/udp
# Enable the firewall
sudo ufw enable
Regularly update your server and Docker:
# Update system packages
sudo apt-get update && sudo apt-get upgrade -y
# Update Docker images
docker -H ssh://docker-web service update --image ${IMAGE}:${IMAGE_TAG} ${STACK_NAME}_app
Check service events and logs:
docker -H ssh://docker-web service ps ${STACK_NAME}_app --no-trunc
docker -H ssh://docker-web service logs ${STACK_NAME}_app
Common issues:
docker -H ssh://docker-web login
docker -H ssh://docker-web node ls
If Caddy/Traefik can’t obtain certificates:
docker -H ssh://docker-web service logs caddy
or traefik_traefik
Test registry connectivity:
# From your local machine
docker pull git.example.com/username/myapp:latest
# From the server
docker -H ssh://docker-web pull git.example.com/username/myapp:latest
You’ve successfully deployed a Yii application to Docker Swarm with:
The Makefile commands simplify the deployment workflow:
make prod-build
- Build the production imagemake prod-push
- Push to the registrymake prod-deploy
- Deploy to Docker SwarmFor more information, see: