-
Notifications
You must be signed in to change notification settings - Fork 0
CI CD
Harpyx ships with a GitHub Actions pipeline that builds the two application images and pushes them to the GitHub Container Registry (ghcr.io).
The pipeline publishes only the Harpyx application images:
ghcr.io/<owner>/harpyx-webappghcr.io/<owner>/harpyx-worker
Backing services such as SQL Server, MinIO, RabbitMQ, OpenSearch, Redis, and ClamAV are not built or published by this pipeline. They may be run as containers, provided directly by the host, or consumed as centralized/shared services depending on the production topology you choose.
The workflow is defined in .github/workflows/deploy.yml and triggers on every push to main (or manually from the Actions tab).
Developer machine
| git push -> main
v
GitHub Actions
|- docker build -> ghcr.io/<owner>/harpyx-webapp:latest
|- docker build -> ghcr.io/<owner>/harpyx-worker:latest
`- images are ready for deployment
Deployment to the production server can then be handled in one of two ways:
- directly by GitHub Actions over SSH
- manually on the server after the images have been published
| File | Purpose |
|---|---|
.github/workflows/deploy.yml |
GitHub Actions workflow: build and push the application images to GHCR |
docker-compose.prod.example.yml |
Example production Compose file. It can be used as a starting point for an all-container deployment, then adapted to the real production topology |
docker-compose.yml |
Development Compose file: builds images locally and exposes the full local stack |
infra/Dockerfile.webapp |
Multi-stage build for Harpyx.WebApp
|
infra/Dockerfile.worker |
Multi-stage build for Harpyx.Worker
|
.env.example |
Template for environment variables: copy to .env, never commit the real .env
|
docker-compose.prod.example.yml is intentionally a convenient baseline, not a mandatory final shape.
You can use it almost as-is if you want to run all backing services as containers on the same host. You can also modify it easily if some services are centralized or managed elsewhere.
Typical adaptations include:
- removing containerized services that are provided externally, such as SQL Server
- changing
Database__Host,Database__Port,Minio__Endpoint,RabbitMQ__HostName,Redis__ConnectionString, orOpenSearch__Endpointto point to shared infrastructure - keeping only
webapp,worker, and the backing containers that this specific deployment should own
For example, if production uses a centralized SQL Server, the real docker-compose.prod.yml can omit the sqlserver service entirely, while webapp and worker connect to the external database through .env.
This section describes a generic Ubuntu-based deployment flow. Exact details depend on whether your backing services are fully containerized or partly centralized.
- Ubuntu 22.04 LTS or later
- Docker Engine installed from the official Docker repository
- A domain name pointed at the server IP
- A GitHub account with write access to the repository
If the docker-compose-plugin package is unavailable from your current apt sources, install the standalone Compose v2 binary:
sudo curl -SL https://github.com/docker/compose/releases/latest/download/docker-compose-linux-x86_64 \
-o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose
docker-compose versionGenerate a dedicated key pair on your development machine. You can leave the passphrase empty for simplicity, or protect the key with a passphrase and store it in the SSH_PASSPHRASE GitHub secret.
# Windows PowerShell
ssh-keygen -t ed25519 -C "github-actions-deploy" -f "$env:USERPROFILE\.ssh\harpyx_deploy"# macOS / Linux
ssh-keygen -t ed25519 -C "github-actions-deploy" -f ~/.ssh/harpyx_deployAuthorize the public key on the server:
# Windows PowerShell
type "$env:USERPROFILE\.ssh\harpyx_deploy.pub" | ssh user@your-server "mkdir -p ~/.ssh && cat >> ~/.ssh/authorized_keys"# macOS / Linux
ssh-copy-id -i ~/.ssh/harpyx_deploy.pub user@your-serverGo to https://github.com/Ryadel/Harpyx/settings/secrets/actions and add the repository secrets required by your deploy workflow.
Typical values are:
| Secret | Value |
|---|---|
SSH_HOST |
Server IP address or hostname |
SSH_USER |
SSH username |
SSH_KEY |
Full content of the deploy private key |
SSH_PORT |
SSH port, usually 22
|
DEPLOY_PATH |
Absolute path on the server, for example /var/www/harpyx
|
GITHUB_TOKEN is provisioned automatically by GitHub Actions and is used by the current workflow to push images to GHCR.
After the first successful Actions run, the packages harpyx-webapp and harpyx-worker appear under the GitHub Packages area for the repository owner.
If those packages remain private, the production server must authenticate to ghcr.io before pulling them. If you prefer public pulls, you can make the packages public instead.
Create the deployment directory and write the production .env file:
mkdir -p /var/www/harpyx
cd /var/www/harpyx
nano .envStart from .env.example and replace every development value with production values.
Some settings are topology-dependent:
-
Database__HostandDatabase__Portshould point to your real SQL Server -
Minio__Endpointshould point either to the local Compose service or to the centralized object storage endpoint -
RabbitMQ__HostName,Redis__ConnectionString, andOpenSearch__Endpointshould follow the same rule
Examples:
# all-container example
Database__Host=sqlserver
Database__Port=1433
Minio__Endpoint=minio:9000
RabbitMQ__HostName=rabbitmq
Redis__ConnectionString=redis:6379
OpenSearch__Endpoint=https://opensearch:9200# centralized/shared services example
Database__Host=sql-prod.internal.example
Database__Port=1433
Minio__Endpoint=minio.internal.example:9000
RabbitMQ__HostName=rabbitmq.internal.example
Redis__ConnectionString=redis.internal.example:6379
OpenSearch__Endpoint=https://opensearch.internal.example:9200Use docker-compose.prod.example.yml as the starting point for your real docker-compose.prod.yml.
If you want a fully containerized production host, you can keep most of the example as-is.
If you use centralized services, simplify it:
- remove services you do not want this stack to own
- remove matching
depends_onentries where appropriate - keep only
webapp,worker, and the backing containers that should actually run on this machine
For example, when SQL Server is centralized, the real production compose can omit the sqlserver service entirely and rely on .env for the external connection details.
If your chosen production topology includes local backing containers, start them before the first application deploy so they can become healthy:
cd /var/www/harpyx
docker compose -f docker-compose.prod.yml up -d
docker compose -f docker-compose.prod.yml logs -fIf some services are centralized, they obviously do not need to be started from this Compose project.
Install Nginx and Certbot:
sudo apt install -y nginx certbot python3-certbot-nginxCreate /etc/nginx/sites-available/harpyx:
server {
server_name yourdomain.com;
location / {
proxy_pass http://localhost:8080;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection keep-alive;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_cache_bypass $http_upgrade;
client_max_body_size 50m;
}
}Enable the site and request a TLS certificate:
sudo ln -s /etc/nginx/sites-available/harpyx /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx
sudo certbot --nginx -d yourdomain.comPush any commit to main, or trigger the workflow manually from the GitHub Actions UI. The pipeline will:
- Build
harpyx-webappandharpyx-worker - Push both images to
ghcr.io - Pull the new images on the server
- Restart
webappandworker
The exact way docker-compose.prod.yml reaches the server is environment-specific. Some teams copy it from CI, others keep it directly on the server or in a separate private infrastructure repository.
Every push to main triggers the pipeline automatically.
Manual equivalent:
cd /var/www/harpyx
docker compose -f docker-compose.prod.yml pull webapp worker
docker compose -f docker-compose.prod.yml up -d --no-deps --remove-orphans webapp worker
docker image prune -f| Symptom | Check |
|---|---|
webapp or worker exits immediately |
Inspect docker compose -f docker-compose.prod.yml logs webapp and verify .env values, service endpoints, and credentials |
docker pull fails with unauthorized
|
The GHCR package is private: either make it public or authenticate the server to ghcr.io
|
| Migrations fail on startup | Verify Database__Host, Database__Port, credentials, reachability, and SQL Server TLS settings |
| OpenSearch health check never passes | Verify password complexity and endpoint/TLS configuration |
| Wrong client IP in logs | Review reverse-proxy forwarding and ReverseProxy__KnownNetworks
|
| ClamAV takes several minutes on first boot | Normal: signature database download can take time |