- Frontend: Next.js 16 + shadcn/ui + Tailwind CSS 4 - Backend: .NET 9 Web API with Npgsql health check - Docker Compose for prod and dev environments - Woodpecker CI pipeline for auto-deploy - Health check endpoints for E2E testing Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
423 lines
10 KiB
Markdown
423 lines
10 KiB
Markdown
# GoodBrick Server Infrastructure
|
|
|
|
## SSH Access
|
|
- **Host:** 31.131.18.254
|
|
- **User:** deploy
|
|
- **Auth:** SSH key (already configured, no password needed)
|
|
- **Command:** `ssh deploy@31.131.18.254`
|
|
|
|
## Server Structure
|
|
|
|
### Base Directory: `/srv`
|
|
|
|
```
|
|
/srv/
|
|
├── apps/ # Empty, ready for applications
|
|
├── gitea/ # Git server
|
|
├── postgres/ # PostgreSQL database
|
|
├── proxy/ # Caddy reverse proxy
|
|
├── uptime-kuma/ # Status monitoring
|
|
└── woodpecker/ # CI/CD server
|
|
```
|
|
|
|
## Docker Network
|
|
- **Network name:** `app-network` (external, shared)
|
|
- All services connected to this network
|
|
- Containers communicate by name (e.g., `postgres`, `gitea`, `caddy`)
|
|
|
|
## Live Services
|
|
|
|
### Production URLs
|
|
- **Gitea:** https://git.goodbrick.com.ua
|
|
- **Uptime Kuma:** https://status.goodbrick.com.ua
|
|
- **Woodpecker CI:** https://ci.goodbrick.com.ua ✅ Working!
|
|
- **Production App:** https://new.goodbrick.com.ua (TODO)
|
|
- **Development App:** https://dev.goodbrick.com.ua (TODO)
|
|
|
|
All domains point to `31.131.18.254` via A records.
|
|
|
|
---
|
|
|
|
## Service Details
|
|
|
|
### 1. Caddy (Reverse Proxy)
|
|
- **Location:** `/srv/proxy`
|
|
- **Container:** `caddy`
|
|
- **Image:** `caddy:2`
|
|
- **Ports:** 80, 443 (public)
|
|
- **Config:** `/srv/proxy/Caddyfile`
|
|
- **SSL:** Automatic via Let's Encrypt
|
|
|
|
#### Caddyfile Routes
|
|
```caddyfile
|
|
git.goodbrick.com.ua {
|
|
reverse_proxy gitea:3000
|
|
}
|
|
|
|
status.goodbrick.com.ua {
|
|
reverse_proxy uptime-kuma:3001
|
|
}
|
|
|
|
ci.goodbrick.com.ua {
|
|
reverse_proxy woodpecker-server:8000 {
|
|
flush_interval -1 # For SSE support
|
|
}
|
|
}
|
|
|
|
new.goodbrick.com.ua {
|
|
reverse_proxy new:3000
|
|
}
|
|
|
|
dev.goodbrick.com.ua {
|
|
reverse_proxy dev:3000
|
|
}
|
|
```
|
|
|
|
**Important Notes:**
|
|
- Use container names (not `127.0.0.1` or `localhost`)
|
|
- `flush_interval -1` required for Woodpecker's Server-Sent Events
|
|
|
|
**Restart:**
|
|
```bash
|
|
ssh deploy@31.131.18.254 'cd /srv/proxy && docker compose restart'
|
|
```
|
|
|
|
---
|
|
|
|
### 2. PostgreSQL
|
|
- **Location:** `/srv/postgres`
|
|
- **Container:** `postgres`
|
|
- **Image:** `postgres:16`
|
|
- **Port:** 127.0.0.1:5432 (localhost only)
|
|
- **Data:** `/srv/postgres/data`
|
|
- **Credentials:** `/srv/postgres/CREDENTIALS.txt` (chmod 600)
|
|
|
|
#### Master Admin
|
|
```
|
|
Host: postgres:5432 (from containers) or 127.0.0.1:5432 (from host)
|
|
User: app
|
|
Password: zYWT5JWu3iAbbW7mOyd1
|
|
Database: app
|
|
```
|
|
|
|
#### Application Databases
|
|
|
|
**Gitea:**
|
|
```
|
|
User: gitea_user
|
|
Password: gitea_pass_zYWT5JWu3iAbbW7m
|
|
Database: gitea_db
|
|
Connection String: postgres://gitea_user:gitea_pass_zYWT5JWu3iAbbW7m@postgres:5432/gitea_db
|
|
```
|
|
|
|
**Production App:**
|
|
```
|
|
User: prod_user
|
|
Password: prod_pass_kL9mN2pQ7xR8sT4v
|
|
Database: prod_db
|
|
Connection String: postgres://prod_user:prod_pass_kL9mN2pQ7xR8sT4v@postgres:5432/prod_db
|
|
```
|
|
|
|
**Development App:**
|
|
```
|
|
User: dev_user
|
|
Password: dev_pass_vB6nM3qP8yW2rT9k
|
|
Database: dev_db
|
|
Connection String: postgres://dev_user:dev_pass_vB6nM3qP8yW2rT9k@postgres:5432/dev_db
|
|
```
|
|
|
|
**Important:**
|
|
- Each app has its own isolated database and user
|
|
- Users cannot access other databases
|
|
- PostgreSQL only accessible via Docker network
|
|
|
|
**Restart:**
|
|
```bash
|
|
ssh deploy@31.131.18.254 'cd /srv/postgres && docker compose restart'
|
|
```
|
|
|
|
---
|
|
|
|
### 3. Gitea (Git Server)
|
|
- **Location:** `/srv/gitea`
|
|
- **Container:** `gitea`
|
|
- **Image:** `gitea/gitea:latest`
|
|
- **URL:** https://git.goodbrick.com.ua
|
|
- **Admin:** admin (set during installation)
|
|
- **Ports:**
|
|
- 127.0.0.1:3002:3000 (HTTP)
|
|
- 127.0.0.1:2222:22 (SSH)
|
|
- **Database:** gitea_db (PostgreSQL)
|
|
- **Data:** `/srv/gitea/data`
|
|
|
|
#### OAuth Application for Woodpecker
|
|
- **Client ID:** 157422cf-7391-4fb0-8f2d-27083676dda6
|
|
- **Redirect URI:** https://ci.goodbrick.com.ua/authorize
|
|
|
|
**Config:**
|
|
```yaml
|
|
environment:
|
|
- GITEA__server__DOMAIN=git.goodbrick.com.ua
|
|
- GITEA__server__ROOT_URL=https://git.goodbrick.com.ua/
|
|
- GITEA__webhook__ALLOWED_HOST_LIST=* # Allows webhooks to Woodpecker
|
|
- GITEA__database__DB_TYPE=postgres
|
|
- GITEA__database__HOST=postgres:5432
|
|
- GITEA__database__NAME=gitea_db
|
|
- GITEA__database__USER=gitea_user
|
|
```
|
|
|
|
**Restart:**
|
|
```bash
|
|
ssh deploy@31.131.18.254 'cd /srv/gitea && docker compose restart'
|
|
```
|
|
|
|
---
|
|
|
|
### 4. Uptime Kuma (Monitoring)
|
|
- **Location:** `/srv/uptime-kuma`
|
|
- **Container:** `uptime-kuma`
|
|
- **Image:** `louislam/uptime-kuma:2`
|
|
- **URL:** https://status.goodbrick.com.ua
|
|
- **Port:** 127.0.0.1:3001:3001
|
|
- **Data:** `/srv/uptime-kuma/data`
|
|
|
|
**Restart:**
|
|
```bash
|
|
ssh deploy@31.131.18.254 'cd /srv/uptime-kuma && docker compose restart'
|
|
```
|
|
|
|
---
|
|
|
|
### 5. Woodpecker CI ✅
|
|
- **Location:** `/srv/woodpecker`
|
|
- **Containers:** `woodpecker-server`, `woodpecker-agent`
|
|
- **Image:** `woodpeckerci/woodpecker-server:latest` (v2.8.3), `woodpeckerci/woodpecker-agent:latest`
|
|
- **URL:** https://ci.goodbrick.com.ua
|
|
- **Port:** 127.0.0.1:8000:8000
|
|
- **Data:** `/srv/woodpecker/data`
|
|
|
|
**Config:**
|
|
```yaml
|
|
environment:
|
|
# Server
|
|
- WOODPECKER_OPEN=true
|
|
- WOODPECKER_HOST=https://ci.goodbrick.com.ua
|
|
- WOODPECKER_GITEA=true
|
|
- WOODPECKER_GITEA_URL=https://git.goodbrick.com.ua
|
|
- WOODPECKER_GITEA_CLIENT=157422cf-7391-4fb0-8f2d-27083676dda6
|
|
- WOODPECKER_GITEA_SECRET=gto_2r3udrnmvtebt37v35bzl375eq5bumxqu2dpwwdfflbumnp5fy7q
|
|
- WOODPECKER_AGENT_SECRET=supersecrettoken123
|
|
- WOODPECKER_ADMIN=admin
|
|
|
|
# Agent
|
|
- WOODPECKER_SERVER=woodpecker-server:9000
|
|
- WOODPECKER_AGENT_SECRET=supersecrettoken123
|
|
```
|
|
|
|
**Status:** ✅ Working! Login with Gitea account.
|
|
|
|
**Important Notes:**
|
|
- Uses OAuth authentication via Gitea
|
|
- Agent connects to server on port 9000 (gRPC)
|
|
- Agent has access to Docker socket for running builds
|
|
- Version 2.8.3 fixes JavaScript security issues (v2.7.2 had SES errors)
|
|
|
|
**Restart:**
|
|
```bash
|
|
ssh deploy@31.131.18.254 'cd /srv/woodpecker && docker compose restart'
|
|
```
|
|
|
|
---
|
|
|
|
## Common Commands
|
|
|
|
### View all containers
|
|
```bash
|
|
ssh deploy@31.131.18.254 'docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"'
|
|
```
|
|
|
|
### View container logs
|
|
```bash
|
|
ssh deploy@31.131.18.254 'docker logs <container_name> --tail 50'
|
|
ssh deploy@31.131.18.254 'docker logs <container_name> -f' # Follow logs
|
|
```
|
|
|
|
### Restart a service
|
|
```bash
|
|
ssh deploy@31.131.18.254 'cd /srv/<service> && docker compose restart'
|
|
```
|
|
|
|
### Restart all services
|
|
```bash
|
|
ssh deploy@31.131.18.254 'cd /srv/postgres && docker compose restart && \
|
|
cd /srv/gitea && docker compose restart && \
|
|
cd /srv/uptime-kuma && docker compose restart && \
|
|
cd /srv/woodpecker && docker compose restart && \
|
|
cd /srv/proxy && docker compose restart'
|
|
```
|
|
|
|
### Access PostgreSQL CLI
|
|
```bash
|
|
ssh deploy@31.131.18.254 'docker exec -it postgres psql -U app -d app'
|
|
```
|
|
|
|
### Check Docker network
|
|
```bash
|
|
ssh deploy@31.131.18.254 'docker network inspect app-network'
|
|
ssh deploy@31.131.18.254 'docker ps --format "{{.Names}}\t{{.Networks}}"'
|
|
```
|
|
|
|
### Test container connectivity
|
|
```bash
|
|
ssh deploy@31.131.18.254 'docker exec <container1> ping -c 2 <container2>'
|
|
```
|
|
|
|
---
|
|
|
|
## Troubleshooting
|
|
|
|
### Issue: Container can't reach another container
|
|
**Symptoms:** "connection refused", "no such host"
|
|
|
|
**Check:**
|
|
```bash
|
|
# Verify all containers are in app-network
|
|
ssh deploy@31.131.18.254 'docker ps --format "{{.Names}}\t{{.Networks}}"'
|
|
```
|
|
|
|
**Solution:**
|
|
1. Ensure all docker-compose.yml files have:
|
|
```yaml
|
|
networks:
|
|
app-network:
|
|
external: true
|
|
```
|
|
2. Restart the affected service
|
|
|
|
---
|
|
|
|
### Issue: "dial tcp: lookup <service> on 127.0.0.11:53: no such host"
|
|
**Cause:** Container not in app-network
|
|
|
|
**Solution:**
|
|
```bash
|
|
ssh deploy@31.131.18.254 'cd /srv/<service> && docker compose down && docker compose up -d'
|
|
```
|
|
|
|
---
|
|
|
|
### Issue: Caddy shows "502 Bad Gateway"
|
|
**Causes:**
|
|
1. Target service is down
|
|
2. Target service not in app-network
|
|
3. Wrong port/container name in Caddyfile
|
|
|
|
**Check:**
|
|
```bash
|
|
# Check if target is running
|
|
ssh deploy@31.131.18.254 'docker ps | grep <service>'
|
|
|
|
# Check Caddy logs
|
|
ssh deploy@31.131.18.254 'docker logs caddy --tail 50'
|
|
|
|
# Test direct connection
|
|
ssh deploy@31.131.18.254 'docker exec caddy wget -O- http://<container>:<port>'
|
|
```
|
|
|
|
---
|
|
|
|
### Issue: Woodpecker shows blank page or JavaScript errors
|
|
**Symptoms:** "SES Removing unpermitted intrinsics", SyntaxError
|
|
|
|
**Solution:**
|
|
1. Update to latest version (2.8.3+)
|
|
2. Ensure Caddy has `flush_interval -1` for SSE support
|
|
3. Clear browser cache (Ctrl+Shift+R)
|
|
|
|
---
|
|
|
|
### Issue: SSL certificate not working
|
|
**Cause:** Usually DNS propagation or Let's Encrypt rate limits
|
|
|
|
**Check:**
|
|
```bash
|
|
# Check Caddy logs
|
|
ssh deploy@31.131.18.254 'docker logs caddy | grep -i certificate'
|
|
|
|
# Check if domain resolves
|
|
nslookup <domain>
|
|
```
|
|
|
|
**Solution:** Wait for DNS propagation (up to 24h) or check DNS A records.
|
|
|
|
---
|
|
|
|
## Security Notes
|
|
|
|
1. **PostgreSQL:**
|
|
- Not exposed to internet (127.0.0.1 only)
|
|
- Separate users for each application
|
|
- Credentials stored in `/srv/postgres/CREDENTIALS.txt` (chmod 600)
|
|
|
|
2. **SSH:**
|
|
- Key-based authentication only
|
|
- User `deploy` has Docker permissions
|
|
|
|
3. **Docker Network:**
|
|
- All containers isolated in `app-network`
|
|
- No direct internet access except through Caddy
|
|
|
|
4. **Secrets:**
|
|
- Never commit credentials to git
|
|
- Store sensitive data in environment variables
|
|
- Use `.env` files (gitignored)
|
|
|
|
---
|
|
|
|
## Next Steps / TODO
|
|
|
|
- [ ] Deploy production app to `new.goodbrick.com.ua` (port 3100)
|
|
- [ ] Deploy development app to `dev.goodbrick.com.ua` (port 3200)
|
|
- [ ] Set up email for Gitea (optional)
|
|
- [ ] Configure Woodpecker pipelines for repositories
|
|
- [ ] Set up automatic backups for PostgreSQL databases
|
|
- [ ] Configure monitoring alerts in Uptime Kuma
|
|
|
|
---
|
|
|
|
## File Locations Summary
|
|
|
|
```
|
|
Server (31.131.18.254):
|
|
├── /srv/
|
|
│ ├── apps/
|
|
│ ├── gitea/
|
|
│ │ ├── docker-compose.yml
|
|
│ │ └── data/
|
|
│ ├── postgres/
|
|
│ │ ├── docker-compose.yml
|
|
│ │ ├── data/
|
|
│ │ └── CREDENTIALS.txt ← All DB credentials
|
|
│ ├── proxy/
|
|
│ │ ├── docker-compose.yml
|
|
│ │ ├── Caddyfile ← All routes
|
|
│ │ ├── data/
|
|
│ │ └── config/
|
|
│ ├── uptime-kuma/
|
|
│ │ ├── docker-compose.yml
|
|
│ │ └── data/
|
|
│ └── woodpecker/
|
|
│ ├── docker-compose.yml
|
|
│ └── data/
|
|
|
|
Local:
|
|
└── C:\Work\goodbrick\GBSite\
|
|
├── SERVER.md ← This file
|
|
└── README.md ← Project overview
|
|
```
|
|
|
|
---
|
|
|
|
**Last Updated:** 2026-02-09
|
|
**Maintained by:** Claude AI Assistant
|