This repo manages the complete AppStore deployment: Docker Compose, Keycloak, Monitoring (Prometheus, Loki, Grafana).
appstore-backend → Python/FastAPI + Celery
appstore-frontend → React + Nginx
appstore-infra → this repo (Compose, Keycloak, Monitoring)
appstore-apps → Ansible app definitions
| Environment | Compose files | URL |
|---|---|---|
| Dev (local) | docker-compose.yml + docker-compose.dev.yml |
http://localhost:3000 |
| Staging | docker-compose.yml + docker-compose.staging.yml |
https://<staging-ip> |
| Production | docker-compose.yml + docker-compose.prod.yml |
https://<prod-ip> |
- Docker + Docker Compose v2
- Git
- Both app repos cloned into the same parent folder as this repo:
dozilab/ ├── appstore-backend/ ├── appstore-frontend/ └── appstore-infra/ ← this repo
git clone git@github.com:your-org/appstore-backend.git
git clone git@github.com:your-org/appstore-frontend.git
git clone git@github.com:your-org/appstore-infra.git
cd appstore-infracp .env.example .envSet these values in .env for local dev:
| Variable | Value |
|---|---|
SERVER_IP |
localhost |
DB_PASSWORD |
anything, e.g. postgres |
KEYCLOAK_ADMIN_PASSWORD |
anything, e.g. admin |
KEYCLOAK_URL |
http://localhost:8080 |
ENCRYPTION_KEY |
generate (see below) |
ANSIBLE_SSH_KEY_PATH |
path to your SSH key |
Generate encryption key:
python3 -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())"ssh-keygen -t ed25519 -f ~/.ssh/dozilab_ansible -N ""docker compose -f docker-compose.yml -f docker-compose.dev.yml up -dThe realm-export.json is already checked in — Keycloak imports it automatically on first start.
Verify at http://localhost:8080 — login with KEYCLOAK_ADMIN_USER / KEYCLOAK_ADMIN_PASSWORD from .env.
If you make changes to the realm, export and commit them:
docker exec -it appstore-infra-keycloak-1 \
/opt/keycloak/bin/kc.sh export \
--dir /tmp/export \
--realm Dozilab \
--users realm_file
docker cp appstore-infra-keycloak-1:/tmp/export/Dozilab-realm.json ./keycloak/realm-export.json
git add keycloak/realm-export.json && git commit -m "update keycloak realm export"| Service | URL |
|---|---|
| Frontend | http://localhost:3000 |
| Backend API | http://localhost:8000/docs |
| Keycloak | http://localhost:8080 |
| Database | localhost:5432 |
| Redis | localhost:6379 |
See heat/README.md for the full server setup guide using OpenStack Heat.
# Staging
ssh -i ~/.ssh/appstore-staging ubuntu@<staging-ip> \
"cd /opt/appstore && git pull && \
docker compose -f docker-compose.yml -f docker-compose.staging.yml pull api celery-worker && \
docker compose -f docker-compose.yml -f docker-compose.staging.yml up -d api celery-worker"
# Production
ssh -i ~/.ssh/appstore-prod ubuntu@<prod-ip> \
"cd /opt/appstore && git pull && \
docker compose -f docker-compose.yml -f docker-compose.prod.yml pull api celery-worker && \
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d api celery-worker"- Push to
main→ automatically deploys to Staging - Push tag
v*→ requires approval → deploys to Production
See .github/workflows/ in the backend and frontend repos.
# View logs
docker compose -f docker-compose.yml -f docker-compose.dev.yml logs -f api
# Run database migrations
docker compose -f docker-compose.yml -f docker-compose.dev.yml exec api alembic upgrade head
# Restart a single service
docker compose -f docker-compose.yml -f docker-compose.dev.yml restart api
# Stop everything (data is preserved)
docker compose -f docker-compose.yml -f docker-compose.dev.yml down
# Stop everything including volumes (WARNING: deletes the database)
docker compose -f docker-compose.yml -f docker-compose.dev.yml down -v| Service | Dev | Staging/Prod |
|---|---|---|
| Grafana | not active | http://<ip>:3000 |
| Prometheus | not active | internal only |
| Loki | not active | internal only |
Grafana datasources (Prometheus + Loki) are provisioned automatically on startup.
Dashboards in monitoring/grafana/dashboards/ are loaded automatically.