add registry setup

This commit is contained in:
root
2026-06-02 13:51:06 -04:00
parent e9dfbeb591
commit da1841e6b9
8 changed files with 155 additions and 29 deletions
+28 -18
View File
@@ -4,23 +4,29 @@ API_DOMAIN=api.example.com
PUBLIC_SITE_DOMAIN=example.com PUBLIC_SITE_DOMAIN=example.com
PGMANAGE_DOMAIN=pgmanage.example.com PGMANAGE_DOMAIN=pgmanage.example.com
PORTAINER_DOMAIN=portainer.example.com PORTAINER_DOMAIN=portainer.example.com
REGISTRY_DOMAIN=registry.rentaldrivego.ma
# ── Optional prebuilt image source ──────────────────────────────────────────── # ── Optional prebuilt image source ────────────────────────────────────────────
# Used for pull-based deployments. CI injects APP_IMAGE and APP_VERSION # Used for pull-based deployments. CI injects APP_IMAGE and APP_VERSION
# automatically during registry-backed deploys, so these can stay commented out. # automatically during registry-backed deploys, so these can stay commented out.
# APP_IMAGE=registry.example.com/rentaldrivego/car_management_system # APP_IMAGE=registry.example.com/rentaldrivego/car_management_system
# APP_VERSION=latest # APP_VERSION=latest
REGISTRY_HOST=registry.rentaldrivego.ma
REGISTRY_USER=rentaldrivego_registry
REGISTRY_PASSWORD=PMPS5k0D7rUeJOk0NkhI5bRtoGjkUqjK
REGISTRY_IMAGE=registry.rentaldrivego.ma/rentaldrivego/car_management_system
# ── Database ────────────────────────────────────────────────────────────────── # ── Database ──────────────────────────────────────────────────────────────────
# Full connection URL (used when DATABASE_URL_FROM_POSTGRES=false) # Full connection URL (used when DATABASE_URL_FROM_POSTGRES=false)
DATABASE_URL=postgresql://postgres:change-me@postgres:5432/rentaldrivego DATABASE_URL=postgresql://postgres:24DY&1u5FLCkNMeiO_p@postgres:5432/rentaldrivego
# Build the URL from individual vars (set to true to use POSTGRES_* vars below) # Build the URL from individual vars (set to true to use POSTGRES_* vars below)
DATABASE_URL_FROM_POSTGRES=true DATABASE_URL_FROM_POSTGRES=true
POSTGRES_HOST=postgres POSTGRES_HOST=postgres
POSTGRES_PORT=5432 POSTGRES_PORT=5432
POSTGRES_DB=rentaldrivego POSTGRES_DB=rentaldrivego
POSTGRES_USER=postgres POSTGRES_USER=postgres
POSTGRES_PASSWORD=change-me POSTGRES_PASSWORD=24DY&1u5FLCkNMeiO_p
# ── Cache ───────────────────────────────────────────────────────────────────── # ── Cache ─────────────────────────────────────────────────────────────────────
REDIS_URL=redis://redis:6379 REDIS_URL=redis://redis:6379
@@ -30,46 +36,50 @@ API_PORT=4000
# SSR server-side calls go via the internal Docker network # SSR server-side calls go via the internal Docker network
API_INTERNAL_URL=http://api:4000/api/v1 API_INTERNAL_URL=http://api:4000/api/v1
# Public-facing API URL visible to browsers — MUST be your real domain, not localhost # Public-facing API URL visible to browsers — MUST be your real domain, not localhost
API_URL=https://api.example.com API_URL=https://api.rentaldrivego.ma
# Baked into Next.js bundles at build time — MUST be set before running `npm run build` # Baked into Next.js bundles at build time — MUST be set before running `npm run build`
NEXT_PUBLIC_API_URL=https://api.example.com/api/v1 NEXT_PUBLIC_API_URL=https://api.rentaldrivego.ma/api/v1
# ── Frontend public URLs ────────────────────────────────────────────────────── # ── Frontend public URLs ──────────────────────────────────────────────────────
# Marketplace sits at the domain root (Traefik routes /dashboard and /admin to their apps) # Marketplace sits at the domain root (Traefik routes /dashboard and /admin to their apps)
NEXT_PUBLIC_MARKETING_URL=https://example.com NEXT_PUBLIC_MARKETING_URL=https://rentaldrivego.ma
NEXT_PUBLIC_MARKETPLACE_URL=https://example.com NEXT_PUBLIC_MARKETPLACE_URL=https://rentaldrivego.ma
NEXT_PUBLIC_DASHBOARD_URL=https://example.com/dashboard NEXT_PUBLIC_DASHBOARD_URL=https://rentaldrivego.ma/dashboard
NEXT_PUBLIC_ADMIN_URL=https://example.com/admin NEXT_PUBLIC_ADMIN_URL=https://rentaldrivego.ma/admin
NEXT_PUBLIC_PUBLIC_SITE_DOMAIN=example.com NEXT_PUBLIC_PUBLIC_SITE_DOMAIN=rentaldrivego.ma
DASHBOARD_URL=https://example.com/dashboard DASHBOARD_URL=https://rentaldrivego.ma/dashboard
ADMIN_URL=https://example.com/admin ADMIN_URL=https://rentaldrivego.ma/admin
# ── CORS ────────────────────────────────────────────────────────────────────── # ── CORS ──────────────────────────────────────────────────────────────────────
# Comma-separated list of allowed browser origins. REQUIRED in production. # Comma-separated list of allowed browser origins. REQUIRED in production.
CORS_ORIGINS=https://example.com,https://www.example.com CORS_ORIGINS=https://example.com,https://www.example.com
# ── Auth ────────────────────────────────────────────────────────────────────── # ── Auth ──────────────────────────────────────────────────────────────────────
JWT_SECRET=replace-with-a-long-random-secret JWT_SECRET=PMPS5k0D7rUeJOk0NkhI5bRtoGjkUqjK
JWT_EXPIRY=8h JWT_EXPIRY=8h
RENTER_JWT_EXPIRY=7d RENTER_JWT_EXPIRY=7d
NODE_ENV=production NODE_ENV=production
# ── Email ───────────────────────────────────────────────────────────────────── # ── Email ─────────────────────────────────────────────────────────────────────
EMAIL_FROM=noreply@example.com EMAIL_FROM=rentaldrivego@gmail.com
EMAIL_FROM_NAME=RentalDriveGo EMAIL_FROM_NAME=RentalDriveGo
# Option A — Resend # Option A — Resend
#RESEND_API_KEY=re_xxxxxxxxxxxxxxxxxxxx #RESEND_API_KEY=C8qPDuFwsv5l@KsGhL/V
# Option B — SMTP (Gmail) # Option B — SMTP (Gmail)
MAIL_HOST=smtp.gmail.com MAIL_HOST=smtp.gmail.com
MAIL_PORT=587 MAIL_PORT=587
MAIL_SCHEME=smtp MAIL_SCHEME=smtp
MAIL_USERNAME=your-smtp-user@example.com MAIL_USERNAME=rentaldrivego@gmail.com
MAIL_PASSWORD=replace-with-an-app-password MAIL_PASSWORD="xmhg ibqy muoc rntc"
MAIL_FROM_ADDRESS=noreply@example.com MAIL_FROM_ADDRESS=rentaldrivego@gmail.com
MAIL_FROM_NAME=RentalDriveGo MAIL_FROM_NAME=RentalDriveGo
MAIL_REPLY_TO_ADDRESS=support@example.com MAIL_REPLY_TO_ADDRESS=rentaldrivego@gmail.com
MAIL_REPLY_TO_NAME=RentalDriveGo MAIL_REPLY_TO_NAME=RentalDriveGo
# ── Firebase push notifications (optional) ──────────────────────────────────── # ── Firebase push notifications (optional) ────────────────────────────────────
# FIREBASE_PROJECT_ID=your-firebase-project-id # FIREBASE_PROJECT_ID=your-firebase-project-id
# FIREBASE_CLIENT_EMAIL=firebase-adminsdk@your-project.iam.gserviceaccount.com # FIREBASE_CLIENT_EMAIL=firebase-adminsdk@your-project.iam.gserviceaccount.com
+1
View File
@@ -4,6 +4,7 @@ API_DOMAIN=api.example.com
PUBLIC_SITE_DOMAIN=example.com PUBLIC_SITE_DOMAIN=example.com
PGMANAGE_DOMAIN=pgmanage.example.com PGMANAGE_DOMAIN=pgmanage.example.com
PORTAINER_DOMAIN=portainer.example.com PORTAINER_DOMAIN=portainer.example.com
REGISTRY_DOMAIN=registry.example.com
# Database # Database
DATABASE_URL_FROM_POSTGRES=true DATABASE_URL_FROM_POSTGRES=true
+6 -6
View File
@@ -108,9 +108,9 @@ build_image:
- docker push $DOCKER_IMAGE - docker push $DOCKER_IMAGE
- docker push $DOCKER_IMAGE_LATEST - docker push $DOCKER_IMAGE_LATEST
rules: rules:
# Skip the container publish step unless either the GitLab registry or an # On the default branch, always attempt the image build so missing registry
# explicit fallback registry configuration is available. # configuration fails loudly in before_script instead of skipping the job.
- if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH && (($CI_REGISTRY && $CI_REGISTRY_USER && $CI_REGISTRY_PASSWORD && $CI_REGISTRY_IMAGE) || ($REGISTRY_HOST && $REGISTRY_USER && $REGISTRY_PASSWORD && $REGISTRY_IMAGE))' - if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH'
- when: never - when: never
# ==================================== # ====================================
@@ -147,10 +147,10 @@ deploy_to_vps:
echo "-- Syncing deployment assets --" echo "-- Syncing deployment assets --"
ssh $VPS_USER@$VPS_IP " ssh $VPS_USER@$VPS_IP "
set -e set -e
mkdir -p '$DEPLOY_ROOT/scripts' '$DEPLOY_ROOT/docker/pgmanage' mkdir -p '$DEPLOY_ROOT/scripts' '$DEPLOY_ROOT/docker/pgmanage' '$DEPLOY_ROOT/docker/registry/auth'
" "
scp docker-compose.production.yml docker-compose.portainer.production.yml traefik.yaml $VPS_USER@$VPS_IP:$DEPLOY_ROOT/ scp docker-compose.production.yml docker-compose.portainer.production.yml docker-compose.registry.production.yml traefik.yaml $VPS_USER@$VPS_IP:$DEPLOY_ROOT/
scp scripts/docker-prod-common.sh scripts/docker-prod-deploy.sh $VPS_USER@$VPS_IP:$DEPLOY_ROOT/scripts/ scp scripts/docker-prod-common.sh scripts/docker-prod-deploy.sh scripts/docker-prod-up-registry.sh $VPS_USER@$VPS_IP:$DEPLOY_ROOT/scripts/
scp docker/pgmanage/override.py $VPS_USER@$VPS_IP:$DEPLOY_ROOT/docker/pgmanage/ scp docker/pgmanage/override.py $VPS_USER@$VPS_IP:$DEPLOY_ROOT/docker/pgmanage/
echo "-- Running deploy script on VPS --" echo "-- Running deploy script on VPS --"
+34
View File
@@ -0,0 +1,34 @@
name: rentaldrivego-registry-prod
services:
registry:
image: registry:2
restart: unless-stopped
networks:
- traefik-proxy
environment:
REGISTRY_HTTP_ADDR: 0.0.0.0:5000
REGISTRY_HTTP_HOST: https://${REGISTRY_DOMAIN}
REGISTRY_HTTP_RELATIVEURLS: "true"
REGISTRY_AUTH: htpasswd
REGISTRY_AUTH_HTPASSWD_REALM: Registry Realm
REGISTRY_AUTH_HTPASSWD_PATH: /auth/htpasswd
REGISTRY_STORAGE_DELETE_ENABLED: "true"
volumes:
- registry_prod_data:/var/lib/registry
- ./docker/registry/auth:/auth:ro
labels:
- traefik.enable=true
- traefik.http.routers.registry.rule=Host(`${REGISTRY_DOMAIN}`)
- traefik.http.routers.registry.entrypoints=websecure
- traefik.http.routers.registry.tls=true
- traefik.http.routers.registry.tls.certresolver=letsencrypt
- traefik.http.routers.registry.service=registry-svc
- traefik.http.services.registry-svc.loadbalancer.server.port=5000
networks:
traefik-proxy:
external: true
volumes:
registry_prod_data:
+43 -5
View File
@@ -7,6 +7,7 @@ Three Docker environments are available:
- `Dockerfile.production` with `docker-compose.production.yml` - `Dockerfile.production` with `docker-compose.production.yml`
- `docker-compose.pgmanage.yml` for a standalone pgManage container - `docker-compose.pgmanage.yml` for a standalone pgManage container
- `docker-compose.portainer.production.yml` for a standalone production Portainer deployment behind Traefik - `docker-compose.portainer.production.yml` for a standalone production Portainer deployment behind Traefik
- `docker-compose.registry.production.yml` for a standalone production Docker registry behind Traefik
### Development ### Development
@@ -123,6 +124,7 @@ Add an A record for every subdomain to your server's public IP before deploying
| `api.rentaldrivego.ma` | API | | `api.rentaldrivego.ma` | API |
| `pgmanage.rentaldrivego.ma` | pgManage (DB admin) | | `pgmanage.rentaldrivego.ma` | pgManage (DB admin) |
| `portainer.rentaldrivego.ma` | Portainer | | `portainer.rentaldrivego.ma` | Portainer |
| `registry.rentaldrivego.ma` | Docker registry |
#### 2. Install Docker and clone the repo #### 2. Install Docker and clone the repo
@@ -163,6 +165,18 @@ Production now derives `DATABASE_URL` inside the app container from `POSTGRES_HO
The example file uses `rentaldrivego.ma` for the marketplace and public site. The dashboard and admin panel are routed under that same host at `/dashboard` and `/admin`. The example file uses `rentaldrivego.ma` for the marketplace and public site. The dashboard and admin panel are routed under that same host at `/dashboard` and `/admin`.
Set `PORTAINER_DOMAIN=portainer.rentaldrivego.ma` in `.env.docker.production` to expose Portainer through Traefik. Set `PORTAINER_DOMAIN=portainer.rentaldrivego.ma` in `.env.docker.production` to expose Portainer through Traefik.
Set `PGMANAGE_DOMAIN=pgmanage.rentaldrivego.ma` to expose pgManage through Traefik, and set `PGMANAGE_LICENSE_KEY` if you are running the enterprise image. Set `PGMANAGE_DOMAIN=pgmanage.rentaldrivego.ma` to expose pgManage through Traefik, and set `PGMANAGE_LICENSE_KEY` if you are running the enterprise image.
Set `REGISTRY_DOMAIN=registry.rentaldrivego.ma` to expose the local Docker registry through Traefik.
If you want to use a self-hosted registry on the same VPS, also set these values in `.env.docker.production`:
```text
REGISTRY_HOST=registry.rentaldrivego.ma
REGISTRY_USER=<registry-user>
REGISTRY_PASSWORD=<strong-registry-password>
REGISTRY_IMAGE=registry.rentaldrivego.ma/rentaldrivego/car_management_system
```
The registry bootstrap script uses `REGISTRY_USER` and `REGISTRY_PASSWORD` to generate the htpasswd file mounted into the registry container, so the same credentials can be used by GitLab CI for `docker login`.
#### 4a. Configure the registry for CI deploys #### 4a. Configure the registry for CI deploys
@@ -173,13 +187,13 @@ If you want GitLab CI to build once and deploy by pulling a prebuilt image on th
- Explicit registry variables: - Explicit registry variables:
Set `REGISTRY_HOST`, `REGISTRY_USER`, `REGISTRY_PASSWORD`, and `REGISTRY_IMAGE` yourself. This works for GitLab Registry, Docker Hub, GHCR, or another OCI registry. Set `REGISTRY_HOST`, `REGISTRY_USER`, `REGISTRY_PASSWORD`, and `REGISTRY_IMAGE` yourself. This works for GitLab Registry, Docker Hub, GHCR, or another OCI registry.
Example explicit values for GitLab's hosted registry: Example explicit values for a local registry exposed through Traefik:
```text ```text
REGISTRY_HOST=gitlab.rentaldrivego.ma:5050 REGISTRY_HOST=registry.rentaldrivego.ma
REGISTRY_IMAGE=gitlab.rentaldrivego.ma:5050/rental_car/car_management_system REGISTRY_IMAGE=registry.rentaldrivego.ma/rentaldrivego/car_management_system
REGISTRY_USER=<deploy-user-or-token-name> REGISTRY_USER=<registry-user>
REGISTRY_PASSWORD=<personal-access-token-or-deploy-token> REGISTRY_PASSWORD=<registry-password>
``` ```
The production compose file reads `APP_IMAGE` and `APP_VERSION` for pull-based deploys. The GitLab deploy job injects those values automatically and now syncs the deployment assets to the VPS before running the server-side deploy script. Production no longer depends on `git pull` during release. The production compose file reads `APP_IMAGE` and `APP_VERSION` for pull-based deploys. The GitLab deploy job injects those values automatically and now syncs the deployment assets to the VPS before running the server-side deploy script. Production no longer depends on `git pull` during release.
@@ -215,6 +229,7 @@ npm run docker:prod:start:admin
npm run docker:prod:start:frontends npm run docker:prod:start:frontends
npm run docker:prod:start:pgmanage npm run docker:prod:start:pgmanage
npm run docker:prod:start:portainer npm run docker:prod:start:portainer
npm run docker:prod:start:registry
npm run docker:prod:start:all npm run docker:prod:start:all
npm run docker:prod:start:api && npm run docker:prod:start:frontends npm run docker:prod:start:api && npm run docker:prod:start:frontends
``` ```
@@ -250,6 +265,29 @@ Equivalent raw Docker Compose command:
docker compose -p rentaldrivego-portainer-prod --env-file .env.docker.production -f docker-compose.portainer.production.yml up -d docker compose -p rentaldrivego-portainer-prod --env-file .env.docker.production -f docker-compose.portainer.production.yml up -d
``` ```
#### 6b. Deploy the local registry
The registry is deployed separately from the main app stack and reuses the shared `traefik-proxy` network managed by Traefik.
```bash
npm run docker:prod:start:registry
```
Equivalent raw Docker Compose command:
```bash
docker compose -p rentaldrivego-registry-prod --env-file .env.docker.production -f docker-compose.registry.production.yml up -d
```
On first start, the script generates `docker/registry/auth/htpasswd` from `REGISTRY_USER` and `REGISTRY_PASSWORD` in `.env.docker.production`. After the registry is up, verify it with:
```bash
docker login registry.rentaldrivego.ma
docker pull registry:2
docker tag registry:2 registry.rentaldrivego.ma/smoke-test:latest
docker push registry.rentaldrivego.ma/smoke-test:latest
```
Uploaded assets handled by the API, including vehicle photos and company branding images, are persisted in the named Docker volume `api_uploads`, mounted inside the API container at `/var/lib/rentaldrivego/storage`. Rebuilding or redeploying the API no longer clears those files as long as that volume is kept. Uploaded assets handled by the API, including vehicle photos and company branding images, are persisted in the named Docker volume `api_uploads`, mounted inside the API container at `/var/lib/rentaldrivego/storage`. Rebuilding or redeploying the API no longer clears those files as long as that volume is kept.
The production helper scripts and the GitLab deploy job now explicitly create the Docker volume `${DOCKER_PROD_PROJECT_NAME:-rentaldrivego-prod}_api_uploads` before starting or redeploying the API stack, so a fresh server bootstrap does not accidentally start the API without its upload storage volume. The production helper scripts and the GitLab deploy job now explicitly create the Docker volume `${DOCKER_PROD_PROJECT_NAME:-rentaldrivego-prod}_api_uploads` before starting or redeploying the API stack, so a fresh server bootstrap does not accidentally start the API without its upload storage volume.
+1
View File
@@ -34,6 +34,7 @@
"docker:prod:start:frontends": "bash scripts/docker-prod-up-frontends.sh", "docker:prod:start:frontends": "bash scripts/docker-prod-up-frontends.sh",
"docker:prod:start:pgmanage": "bash scripts/docker-prod-up-pgmanage.sh", "docker:prod:start:pgmanage": "bash scripts/docker-prod-up-pgmanage.sh",
"docker:prod:start:portainer": "bash scripts/docker-prod-up-portainer.sh", "docker:prod:start:portainer": "bash scripts/docker-prod-up-portainer.sh",
"docker:prod:start:registry": "bash scripts/docker-prod-up-registry.sh",
"docker:prod:deploy": "bash scripts/docker-prod-deploy.sh", "docker:prod:deploy": "bash scripts/docker-prod-deploy.sh",
"docker:prod:up": "bash scripts/docker-prod-up-all.sh", "docker:prod:up": "bash scripts/docker-prod-up-all.sh",
"docker:prod:down": "docker compose -p rentaldrivego-prod --env-file .env.docker.production -f docker-compose.production.yml down", "docker:prod:down": "docker compose -p rentaldrivego-prod --env-file .env.docker.production -f docker-compose.production.yml down",
+32
View File
@@ -5,13 +5,17 @@ set -euo pipefail
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
PROD_COMPOSE_FILE="${ROOT_DIR}/docker-compose.production.yml" PROD_COMPOSE_FILE="${ROOT_DIR}/docker-compose.production.yml"
PORTAINER_COMPOSE_FILE="${ROOT_DIR}/docker-compose.portainer.production.yml" PORTAINER_COMPOSE_FILE="${ROOT_DIR}/docker-compose.portainer.production.yml"
REGISTRY_COMPOSE_FILE="${ROOT_DIR}/docker-compose.registry.production.yml"
TRAEFIK_COMPOSE_FILE="${ROOT_DIR}/traefik.yaml" TRAEFIK_COMPOSE_FILE="${ROOT_DIR}/traefik.yaml"
ENV_FILE="${ROOT_DIR}/.env.docker.production" ENV_FILE="${ROOT_DIR}/.env.docker.production"
PROJECT_NAME="${DOCKER_PROD_PROJECT_NAME:-rentaldrivego-prod}" PROJECT_NAME="${DOCKER_PROD_PROJECT_NAME:-rentaldrivego-prod}"
PORTAINER_PROJECT_NAME="${DOCKER_PORTAINER_PROJECT_NAME:-rentaldrivego-portainer-prod}" PORTAINER_PROJECT_NAME="${DOCKER_PORTAINER_PROJECT_NAME:-rentaldrivego-portainer-prod}"
REGISTRY_PROJECT_NAME="${DOCKER_REGISTRY_PROJECT_NAME:-rentaldrivego-registry-prod}"
TRAEFIK_NETWORK="${DOCKER_PROD_TRAEFIK_NETWORK:-traefik-proxy}" TRAEFIK_NETWORK="${DOCKER_PROD_TRAEFIK_NETWORK:-traefik-proxy}"
VOLUME_BACKUP_IMAGE="${DOCKER_VOLUME_BACKUP_IMAGE:-postgres:16-alpine}" VOLUME_BACKUP_IMAGE="${DOCKER_VOLUME_BACKUP_IMAGE:-postgres:16-alpine}"
API_UPLOADS_VOLUME="${DOCKER_PROD_API_UPLOADS_VOLUME:-${PROJECT_NAME}_api_uploads}" API_UPLOADS_VOLUME="${DOCKER_PROD_API_UPLOADS_VOLUME:-${PROJECT_NAME}_api_uploads}"
REGISTRY_AUTH_DIR="${ROOT_DIR}/docker/registry/auth"
REGISTRY_AUTH_FILE="${REGISTRY_AUTH_DIR}/htpasswd"
ensure_env_file() { ensure_env_file() {
if [[ ! -f "${ENV_FILE}" ]]; then if [[ ! -f "${ENV_FILE}" ]]; then
@@ -43,6 +47,10 @@ portainer_compose() {
docker compose -p "${PORTAINER_PROJECT_NAME}" --env-file "${ENV_FILE}" -f "${PORTAINER_COMPOSE_FILE}" "$@" docker compose -p "${PORTAINER_PROJECT_NAME}" --env-file "${ENV_FILE}" -f "${PORTAINER_COMPOSE_FILE}" "$@"
} }
registry_compose() {
docker compose -p "${REGISTRY_PROJECT_NAME}" --env-file "${ENV_FILE}" -f "${REGISTRY_COMPOSE_FILE}" "$@"
}
traefik_compose() { traefik_compose() {
docker compose --env-file "${ENV_FILE}" -f "${TRAEFIK_COMPOSE_FILE}" "$@" docker compose --env-file "${ENV_FILE}" -f "${TRAEFIK_COMPOSE_FILE}" "$@"
} }
@@ -141,6 +149,30 @@ start_portainer() {
portainer_compose up -d portainer_compose up -d
} }
ensure_registry_auth_file() {
mkdir -p "${REGISTRY_AUTH_DIR}"
if [[ -n "${REGISTRY_USER:-}" && -n "${REGISTRY_PASSWORD:-}" ]]; then
docker run --rm --entrypoint htpasswd httpd:2-alpine -Bbn "${REGISTRY_USER}" "${REGISTRY_PASSWORD}" > "${REGISTRY_AUTH_FILE}"
chmod 600 "${REGISTRY_AUTH_FILE}"
return 0
fi
if [[ -f "${REGISTRY_AUTH_FILE}" ]]; then
return 0
fi
echo "Set REGISTRY_USER and REGISTRY_PASSWORD in .env.docker.production or create ${REGISTRY_AUTH_FILE} manually." >&2
exit 1
}
start_registry() {
ensure_env_file
ensure_traefik_network
ensure_registry_auth_file
registry_compose up -d
}
start_traefik() { start_traefik() {
ensure_env_file ensure_env_file
ensure_traefik_network ensure_traefik_network
+10
View File
@@ -0,0 +1,10 @@
#!/usr/bin/env bash
set -euo pipefail
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
source "${ROOT_DIR}/scripts/docker-prod-common.sh"
start_registry
echo "Registry is starting."