Set up a CI/CD pipeline using Gitea Actions and Docker to build, test, and deploy the Initiative app to a VPS at https://initiative.dostulata.rocks. The workflow runs quality checks on every push and builds/publishes a Docker image on semver tags. The VPS serves the static SPA behind Caddy (automatic HTTPS).
Acceptance Criteria
Dockerfile produces a working container that serves the SPA
Gitea Actions workflow runs pnpm check on every push
Workflow builds and pushes a Docker image to the Gitea registry on semver tags
3.2 Create a non-root user (optional but recommended)
adduser deploy
usermod -aG docker deploy
Then log in as deploy for the remaining steps, or continue as root.
4. Set up Caddy (reverse proxy + automatic HTTPS)
Caddy runs directly on the VPS (not in Docker) and proxies requests to your app container. It handles HTTPS certificates from Let's Encrypt automatically.
Make sure you're logged in: docker login git.bahamut.nitrix.one
Check token has read:package scope
Container not starting:
Check logs: cd /opt/initiative && docker compose logs
## Summary
Set up a CI/CD pipeline using Gitea Actions and Docker to build, test, and deploy the Initiative app to a VPS at https://initiative.dostulata.rocks. The workflow runs quality checks on every push and builds/publishes a Docker image on semver tags. The VPS serves the static SPA behind Caddy (automatic HTTPS).
## Acceptance Criteria
- [x] Dockerfile produces a working container that serves the SPA
- [x] Gitea Actions workflow runs `pnpm check` on every push
- [x] Workflow builds and pushes a Docker image to the Gitea registry on semver tags
- [x] VPS serves the app at https://initiative.dostulata.rocks with automatic HTTPS
- [x] A setup guide documents all manual VPS steps (runner, Caddy, Docker)
<details>
<summary>VPS Setup Guide</summary>
# VPS Setup Guide — Initiative
This guide walks you through setting up a VPS to host the Initiative app at `initiative.dostulata.rocks`.
## 1. Get a VPS
Recommended: **Hetzner Cloud CX22** (~4 EUR/mo) — 2 vCPU, 4GB RAM, 40GB disk.
Any provider works (DigitalOcean, Contabo, Netcup, etc). Requirements:
- Ubuntu 24.04 LTS (or Debian 12)
- Public IPv4 address
- SSH access
## 2. Point your domain
In your domain registrar's DNS settings, create:
```
Type: A
Name: @ (or leave blank, depending on registrar)
Value: <YOUR_VPS_IP>
TTL: 300
```
If you also want `www.dostulata.rocks`:
```
Type: CNAME
Name: www
Value: dostulata.rocks
TTL: 300
```
Verify propagation (may take a few minutes):
```bash
dig +short dostulata.rocks
# Should return your VPS IP
```
## 3. Initial VPS setup
SSH into your VPS:
```bash
ssh root@<YOUR_VPS_IP>
```
### 3.1 Update system and install Docker
```bash
apt update && apt upgrade -y
# Install Docker (official method)
curl -fsSL https://get.docker.com | sh
# Verify
docker --version
docker compose version
```
### 3.2 Create a non-root user (optional but recommended)
```bash
adduser deploy
usermod -aG docker deploy
```
Then log in as `deploy` for the remaining steps, or continue as root.
## 4. Set up Caddy (reverse proxy + automatic HTTPS)
Caddy runs directly on the VPS (not in Docker) and proxies requests to your app container. It handles HTTPS certificates from Let's Encrypt automatically.
### 4.1 Install Caddy
```bash
apt install -y debian-keyring debian-archive-keyring apt-transport-https curl
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | tee /etc/apt/sources.list.d/caddy-stable.list
apt update
apt install caddy
```
### 4.2 Configure Caddy
```bash
cat > /etc/caddy/Caddyfile << 'EOF'
dostulata.rocks {
reverse_proxy localhost:8080
}
EOF
```
That's it. Caddy will automatically obtain and renew HTTPS certificates.
### 4.3 Start Caddy
```bash
systemctl enable caddy
systemctl restart caddy
```
## 5. Set up Gitea Actions Runner
The runner executes your CI/CD workflows on this VPS.
### 5.1 Download act_runner
Check the latest version at https://gitea.com/gitea/act_runner/releases, then:
```bash
# Replace VERSION with the latest (e.g. 0.2.11)
VERSION=0.2.11
curl -L -o /usr/local/bin/act_runner \
"https://gitea.com/gitea/act_runner/releases/download/v${VERSION}/act_runner-${VERSION}-linux-amd64"
chmod +x /usr/local/bin/act_runner
```
### 5.2 Get a registration token
1. Go to `https://git.bahamut.nitrix.one/dostulata/initiative/settings/actions/runners`
2. Click "Create new Runner"
3. Copy the registration token
### 5.3 Register the runner
```bash
act_runner register \
--instance https://git.bahamut.nitrix.one \
--token <YOUR_REGISTRATION_TOKEN> \
--name initiative-runner \
--labels ubuntu-latest:docker://node:22
```
The `--labels` flag maps the `ubuntu-latest` label (used in workflows) to a Docker image the runner will use.
### 5.4 Run as a systemd service
```bash
cat > /etc/systemd/system/act_runner.service << 'EOF'
[Unit]
Description=Gitea Actions Runner
After=docker.service
[Service]
Type=simple
User=root
WorkingDirectory=/root
ExecStart=/usr/local/bin/act_runner daemon
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.target
EOF
systemctl daemon-reload
systemctl enable act_runner
systemctl start act_runner
```
Verify it's running:
```bash
systemctl status act_runner
```
You should also see the runner appear as "Online" in your Gitea repo's runner settings.
## 6. Set up the app container
### 6.1 Create an app directory
```bash
mkdir -p /opt/initiative
```
### 6.2 Create docker-compose.yml
```bash
cat > /opt/initiative/docker-compose.yml << 'EOF'
services:
web:
image: git.bahamut.nitrix.one/dostulata/initiative:latest
restart: unless-stopped
ports:
- "8080:80"
EOF
```
This maps port 8080 (what Caddy proxies to) to port 80 inside the container (where Nginx serves the static files).
### 6.3 Log in to the Gitea container registry
```bash
docker login git.bahamut.nitrix.one
# Username: your Gitea username
# Password: a personal access token with package:read scope
```
To create a token: Gitea > Settings > Applications > Generate New Token (enable `read:package`).
### 6.4 First deploy (once the CI has pushed an image)
```bash
cd /opt/initiative
docker compose pull
docker compose up -d
```
## 7. Deploy new versions
After the CI builds and pushes a new image (triggered by a git tag), update the running container:
```bash
cd /opt/initiative
docker compose pull
docker compose up -d
```
### Optional: Auto-deploy with Watchtower
To automatically pull and restart when a new `latest` image is pushed:
```bash
mkdir -p /opt/watchtower
cat > /opt/watchtower/docker-compose.yml << 'EOF'
services:
watchtower:
image: containrrr/watchtower
restart: unless-stopped
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- /root/.docker/config.json:/config.json:ro
command: --interval 300 --cleanup
EOF
cd /opt/watchtower
docker compose up -d
```
Watchtower checks every 5 minutes for new images and auto-deploys.
## 8. Verify everything works
1. Push a semver tag to trigger the CI:
```bash
git tag 0.1.0
git push origin 0.1.0
```
2. Watch the workflow run at `https://git.bahamut.nitrix.one/dostulata/initiative/actions`
3. Once it completes, the image is in the registry
4. Pull and start on the VPS (or Watchtower does it automatically)
5. Visit `https://initiative.dostulata.rocks` — you should see your app with HTTPS
## Troubleshooting
**Caddy not getting certificates:**
- Make sure ports 80 and 443 are open in your VPS firewall
- Make sure DNS is pointing to the VPS: `dig +short dostulata.rocks`
- Check logs: `journalctl -u caddy`
**Runner not picking up jobs:**
- Check status: `systemctl status act_runner`
- Check Gitea: repo Settings > Actions > Runners (should show "Online")
- Check logs: `journalctl -u act_runner`
**Docker pull failing:**
- Make sure you're logged in: `docker login git.bahamut.nitrix.one`
- Check token has `read:package` scope
**Container not starting:**
- Check logs: `cd /opt/initiative && docker compose logs`
</details>
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Summary
Set up a CI/CD pipeline using Gitea Actions and Docker to build, test, and deploy the Initiative app to a VPS at https://initiative.dostulata.rocks. The workflow runs quality checks on every push and builds/publishes a Docker image on semver tags. The VPS serves the static SPA behind Caddy (automatic HTTPS).
Acceptance Criteria
pnpm checkon every pushVPS Setup Guide
VPS Setup Guide — Initiative
This guide walks you through setting up a VPS to host the Initiative app at
initiative.dostulata.rocks.1. Get a VPS
Recommended: Hetzner Cloud CX22 (~4 EUR/mo) — 2 vCPU, 4GB RAM, 40GB disk.
Any provider works (DigitalOcean, Contabo, Netcup, etc). Requirements:
2. Point your domain
In your domain registrar's DNS settings, create:
If you also want
www.dostulata.rocks:Verify propagation (may take a few minutes):
3. Initial VPS setup
SSH into your VPS:
3.1 Update system and install Docker
3.2 Create a non-root user (optional but recommended)
Then log in as
deployfor the remaining steps, or continue as root.4. Set up Caddy (reverse proxy + automatic HTTPS)
Caddy runs directly on the VPS (not in Docker) and proxies requests to your app container. It handles HTTPS certificates from Let's Encrypt automatically.
4.1 Install Caddy
4.2 Configure Caddy
That's it. Caddy will automatically obtain and renew HTTPS certificates.
4.3 Start Caddy
5. Set up Gitea Actions Runner
The runner executes your CI/CD workflows on this VPS.
5.1 Download act_runner
Check the latest version at https://gitea.com/gitea/act_runner/releases, then:
5.2 Get a registration token
https://git.bahamut.nitrix.one/dostulata/initiative/settings/actions/runners5.3 Register the runner
The
--labelsflag maps theubuntu-latestlabel (used in workflows) to a Docker image the runner will use.5.4 Run as a systemd service
Verify it's running:
You should also see the runner appear as "Online" in your Gitea repo's runner settings.
6. Set up the app container
6.1 Create an app directory
6.2 Create docker-compose.yml
This maps port 8080 (what Caddy proxies to) to port 80 inside the container (where Nginx serves the static files).
6.3 Log in to the Gitea container registry
To create a token: Gitea > Settings > Applications > Generate New Token (enable
read:package).6.4 First deploy (once the CI has pushed an image)
7. Deploy new versions
After the CI builds and pushes a new image (triggered by a git tag), update the running container:
Optional: Auto-deploy with Watchtower
To automatically pull and restart when a new
latestimage is pushed:Watchtower checks every 5 minutes for new images and auto-deploys.
8. Verify everything works
https://git.bahamut.nitrix.one/dostulata/initiative/actionshttps://initiative.dostulata.rocks— you should see your app with HTTPSTroubleshooting
Caddy not getting certificates:
dig +short dostulata.rocksjournalctl -u caddyRunner not picking up jobs:
systemctl status act_runnerjournalctl -u act_runnerDocker pull failing:
docker login git.bahamut.nitrix.oneread:packagescopeContainer not starting:
cd /opt/initiative && docker compose logs