Docker Compose
This guide covers deploying the Expose server using Docker Compose.
Directory Structure
/opt/expose/├── deploy/│ ├── docker-compose.yml│ ├── .env│ └── server/│ └── Dockerfile└── migrations/ └── 001_initial_schema.sqldocker-compose.yml
The default docker-compose.yml is located in the deploy/ directory:
services: expose-server: build: context: .. dockerfile: deploy/server/Dockerfile restart: always environment: - DATABASE_URL=${DATABASE_URL} - JWT_SECRET=${JWT_SECRET} - BASE_DOMAIN=${BASE_DOMAIN} - WEBHOOK_SECRET=${WEBHOOK_SECRET} - RUST_LOG=info depends_on: postgres: condition: service_healthy volumes: - /var/run/docker.sock:/var/run/docker.sock networks: - web - backend - expose_user_apps labels: # API routes - "traefik.enable=true" - "traefik.http.routers.expose-api.rule=Host(`api.${BASE_DOMAIN}`)" - "traefik.http.routers.expose-api.entrypoints=websecure" - "traefik.http.routers.expose-api.tls.certresolver=letsencrypt"
# Tunnel routes (wildcard) - "traefik.http.routers.expose-tunnel.rule=HostRegexp(`{subdomain:[a-z0-9-]+}.expose.${BASE_DOMAIN}`)" - "traefik.http.routers.expose-tunnel.entrypoints=websecure" - "traefik.http.routers.expose-tunnel.tls.certresolver=letsencrypt" - "traefik.http.routers.expose-tunnel.tls.domains[0].main=expose.${BASE_DOMAIN}" - "traefik.http.routers.expose-tunnel.tls.domains[0].sans=*.expose.${BASE_DOMAIN}"
# Hosted routes (wildcard) - "traefik.http.routers.expose-hosted.rule=HostRegexp(`{subdomain:[a-z0-9-]+}.hosted.${BASE_DOMAIN}`)" - "traefik.http.routers.expose-hosted.entrypoints=websecure" - "traefik.http.routers.expose-hosted.tls.certresolver=letsencrypt" - "traefik.http.routers.expose-hosted.tls.domains[0].main=hosted.${BASE_DOMAIN}" - "traefik.http.routers.expose-hosted.tls.domains[0].sans=*.hosted.${BASE_DOMAIN}"
- "traefik.docker.network=web"
postgres: image: postgres:16-alpine restart: always environment: - POSTGRES_USER=expose - POSTGRES_PASSWORD=${POSTGRES_PASSWORD} - POSTGRES_DB=expose volumes: - postgres_data:/var/lib/postgresql/data networks: - backend healthcheck: test: ["CMD-SHELL", "pg_isready -U expose"] interval: 5s timeout: 5s retries: 5
registry: image: registry:2 restart: always volumes: - registry_data:/var/lib/registry networks: - backend
volumes: postgres_data: registry_data:
networks: web: external: true backend: internal: true expose_user_apps: name: expose_user_appsDeploying
1. Clone the Repository
git clone https://github.com/basique-industrie/expose /opt/exposecd /opt/expose/deploy2. Configure Environment
cp .env.example .envnano .env3. Create Networks
docker network create webdocker network create expose_user_apps4. Start Services
docker compose up -d5. Check Status
# View running containersdocker compose ps
# View logsdocker compose logs -f expose-server
# Check healthcurl https://api.yourdomain.com/healthUpdating
To update the server:
cd /opt/exposegit pullcd deploydocker compose build expose-serverdocker compose up -d expose-serverScaling
For high availability, you can run multiple instances behind a load balancer. Note that tunnel connections are stateful, so sticky sessions are recommended.
Backup
Database
# Backupdocker compose exec postgres pg_dump -U expose expose > backup.sql
# Restorecat backup.sql | docker compose exec -T postgres psql -U expose exposeRegistry
The registry data is stored in a Docker volume. Back it up:
docker run --rm -v expose_registry_data:/data -v $(pwd):/backup alpine tar czf /backup/registry-backup.tar.gz -C /data .