This directory contains Terraform configuration for provisioning Hetzner Cloud infrastructure and the CD pipeline setup for automated deployments.
infra/
├── modules/ # Reusable Terraform modules
├── environments/ # Environment-specific configurations
└── scripts/ # Deployment scripts
└── deploy-to-server.sh # Server deployment script (used by CD pipeline)
- Quick Start
- Prerequisites
- Infrastructure Setup
- Configuration
- Server Setup
- CD Pipeline Setup
- Deployment
- Scaling
- Troubleshooting
-
Configure GitHub secrets (see CD Pipeline Setup)
-
Push to main branch: The CD pipeline will automatically:
- Provision infrastructure (if needed)
- Build and push Docker image
- Deploy to all servers
That's it! Everything is fully automated.
- Hetzner Cloud Account: Sign up at https://www.hetzner.com/cloud
- Hetzner API Token: Create a token at https://console.hetzner.com/projects/{project-id}/security/tokens
- Terraform: Install Terraform >= 1.0 from https://www.terraform.io/downloads
- GitHub Repository: Your code should be in a GitHub repository
- SSH Key Pair: Generate if you don't have one:
ssh-keygen -t ed25519 -C "your_email@example.com"
Infrastructure provisioning is fully automated by the CD pipeline. When you push to main or master, the pipeline will:
- Initialize Terraform
- Plan infrastructure changes
- Apply infrastructure (create/update servers)
- Get server IPs from outputs
- Deploy application to all servers
No manual Terraform commands needed! Just configure GitHub secrets and push.
If you need to run Terraform manually (e.g., for troubleshooting):
cd infra/environments/production
terraform init
terraform plan
terraform applyNote: The CD pipeline uses GitHub secrets (HETZNER_TOKEN, HETZNER_SSH_PUBLIC_KEY) instead of terraform.tfvars. For manual runs, you'll need to set these as environment variables or use -var flags.
The CD pipeline automatically provisions infrastructure using GitHub secrets. Configuration is hardcoded in environments/production/main.tf.
HETZNER_TOKEN: Hetzner Cloud API tokenHETZNER_SSH_PUBLIC_KEY: SSH public key for server access
project_name:"quantarded"server_type:"cx23"server_image:"ubuntu-22.04"server_location:"nbg1"instance_count:1
To change configuration, edit environments/production/main.tf directly.
If running Terraform manually, set environment variables:
export TF_VAR_hetzner_token="your-hetzner-cloud-api-token"
export TF_VAR_ssh_public_key="ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAI..."
export AWS_ACCESS_KEY_ID="your-aws-key" # If using S3 backend
export AWS_SECRET_ACCESS_KEY="your-aws-secret" # If using S3 backendServer setup is fully automated by the CD pipeline. The cloud-init script creates the necessary directory structure and installs Docker, and the deployment pipeline automatically:
- Creates
docker-compose.ymlif it doesn't exist - Updates the Docker image reference with the latest build
- Creates
.envfile from GitHub Actions secrets automatically - Stops and removes old containers to prevent stale resources
- Cleans up old Docker images
- Pulls the latest image and starts the service
All configuration is done via GitHub Actions secrets. The deployment pipeline automatically creates the .env file on each server using the secrets you configure in GitHub Actions (see CD Pipeline Setup).
No manual server setup is required - just configure the GitHub secrets and push to trigger deployment.
Each deployment automatically:
- Stops and removes old containers (
docker-compose down --remove-orphans) - Removes old images with the same repository name
- Cleans up unused Docker resources (
docker system prune -f --volumes)
This ensures no stale resources accumulate on the server.
The CD pipeline automatically builds Docker images and deploys to Hetzner servers.
Go to: Repository → Settings → Secrets and variables → Actions
Add these secrets:
HETZNER_TOKEN Your Hetzner Cloud API token
HETZNER_SSH_PRIVATE_KEY
Your SSH private key (contents of ~/.ssh/id_ed25519):
cat ~/.ssh/id_ed25519
# Copy the entire outputHETZNER_SSH_PUBLIC_KEY
Your SSH public key (contents of ~/.ssh/id_ed25519.pub):
cat ~/.ssh/id_ed25519.pub
# Copy the entire outputAWS_ACCESS_KEY_ID (if using S3 backend) AWS access key ID for S3 backend access
AWS_SECRET_ACCESS_KEY (if using S3 backend) AWS secret access key for S3 backend access
Note:
- Infrastructure is automatically provisioned/updated on each push
- Server IPs are automatically discovered from Terraform outputs
- When you scale up or down, the pipeline automatically detects and applies changes
GITHUB_TOKEN (automatically available)
GitHub token for pulling private Docker images from GHCR. The default GITHUB_TOKEN is automatically available in GitHub Actions workflows. If your repository/package is private and you encounter authentication issues, you may need to create a Personal Access Token with read:packages permission and add it as a secret.
LLM_API_KEY OpenAI API key for Reddit scraper (required if Reddit scraper is enabled)
TINYBIRD_TOKEN Tinybird API token (required)
QUIVER_API_KEY Quiver Quant API key (required if Quiver scraper is enabled)
Configuration values are hardcoded in docker-compose.yml and can be modified by editing the deployment workflow (.github/workflows/cd.yml). Current defaults:
REDDIT_SCRAPER_ENABLED:trueTIME_WINDOW_MINUTES:15SCRAPER_INTERVAL_MINUTES:5CLASSIFY_CONCURRENCY:3QUIVER_SCRAPER_ENABLED:falseQUIVER_SCRAPER_CRON:0 */6 * * *
To change these values, edit the docker-compose.yml section in .github/workflows/cd.yml.
The CD pipeline automatically:
- Builds Docker images on push to
main/master - Pushes to GitHub Container Registry (GHCR)
- Deploys to configured Hetzner servers
Make sure:
- Package visibility is set correctly: Repository → Packages → quantarded → Package settings
- The image name in
docker-compose.ymlmatches your GitHub repository
The CD pipeline automatically deploys on push to main or master branch:
-
Push your code:
git push origin main
-
Monitor deployment:
- Go to: Repository → Actions
- Watch the "CD Pipeline" workflow
ssh root@<server-ip>
cd /opt/quantarded
# Check running containers
docker-compose ps
# View logs
docker-compose logs -f
# Check specific container logs
docker logs quantarded-scraper -f- Update Terraform configuration:
- Edit
environments/production/main.tf: Changeinstance_count = 2 - Push to
mainbranch - CD pipeline will automatically provision the new server and deploy to it
- Edit
To change server type, image, or location:
-
Edit
environments/production/main.tf:server_type = "cx31" # Upgrade to larger instance server_location = "fsn1" # Change location
-
Push to main branch: CD pipeline will apply changes automatically
Note: Changing server type or location will recreate the server. Make sure to backup data first.
Application configuration is hardcoded in infra/scripts/deploy-to-server.sh. To change:
- Edit the script file
- Push to main branch
- CD pipeline will automatically deploy with new configuration
Infrastructure configuration is in environments/production/main.tf. To change:
- Edit the file (e.g., change
instance_count,server_type, etc.) - Push to main branch
- CD pipeline will automatically apply changes
Check application health:
# View logs
docker logs quantarded-scraper -f
# Check container status
docker ps
# View resource usage
docker stats
# Check docker-compose status
docker-compose psTo rollback to a previous version:
ssh root@<server-ip>
cd /opt/quantarded
export IMAGE_TAG=<previous-sha-or-tag>
docker-compose pull
docker-compose up -dTo rollback via CD pipeline, push a commit that references the previous image tag, or manually trigger the workflow with a specific commit SHA.
If you get authentication errors when pulling from GHCR:
-
Login to GHCR on the server:
echo $GITHUB_TOKEN | docker login ghcr.io -u USERNAME --password-stdin
Or create a GitHub Personal Access Token with
read:packagespermission. -
Check package visibility: Repository → Packages → quantarded → Package settings
- Check GitHub Actions logs: Repository → Actions → CD Pipeline
- Verify SSH key: Test connection manually:
ssh root@<server-ip> - Check server setup: Ensure docker-compose.yml and .env exist
-
Check logs:
docker logs quantarded-scraper
-
Verify .env file: Ensure all required variables are set
-
Check docker-compose.yml syntax:
docker-compose config
-
Check disk space:
df -h docker system df
-
Check SSH key:
ssh-add -l # Should show your key -
Test connection:
ssh root@<server-ip>
-
Verify key in GitHub secrets: Check that
HETZNER_SSH_PUBLIC_KEYmatches your public key
- Invalid credentials: Verify
HETZNER_TOKENin GitHub secrets - Resource conflicts: Run
terraform planto see what will change - State lock: If terraform is stuck, check for
.terraform.tfstate.lock.infoand remove if safe - Module not found: Make sure you're running terraform from the environment directory (
environments/production)
To destroy all resources:
cd infra/environments/production
terraform destroyWarning: This will delete all servers and data. Make sure you have backups!
See modules/server/README.md for detailed module documentation.
The modules/server module creates Hetzner Cloud servers with firewall rules and SSH key management.
module "servers" {
source = "../../modules/server"
project_name = "quantarded"
server_type = "cx23"
server_image = "ubuntu-22.04"
server_location = "nbg1"
instance_count = 1
ssh_public_key = var.ssh_public_key
}| Name | Description | Type | Required |
|---|---|---|---|
| project_name | Project name (used for resource naming) | string |
yes |
| server_type | Hetzner server type | string |
yes |
| server_image | Server image (OS) | string |
yes |
| server_location | Server location | string |
yes |
| instance_count | Number of server instances to create | number |
yes |
| ssh_public_key | SSH public key for server access | string |
yes |
| Name | Description |
|---|---|
| server_ips | Public IP addresses of the servers |
| server_names | Names of the servers |
| ssh_command | SSH command to connect to the first server |
| deployment_info | Deployment information with server details |
| firewall_id | Firewall ID |
| ssh_key_id | SSH key ID |
hcloud_ssh_key: SSH key for server accesshcloud_firewall: Firewall with rules for SSH (22), HTTP (80), HTTPS (443)hcloud_server: Server instances with cloud-init configuration
- templates/docker-compose.yml.template - Production docker-compose template (reference only)
- ../.github/workflows/cd.yml - CD pipeline workflow