From fc4d099d4f0697ffd81f5109920e446ab856c29a Mon Sep 17 00:00:00 2001 From: root Date: Fri, 22 May 2026 12:22:14 -0400 Subject: [PATCH] add portainer --- DOCKER.md | 28 ++++++++++++++++++++++++++++ package.json | 1 + scripts/docker-prod-common.sh | 12 ++++++++++++ scripts/docker-prod-up-portainer.sh | 10 ++++++++++ 4 files changed, 51 insertions(+) create mode 100644 scripts/docker-prod-up-portainer.sh diff --git a/DOCKER.md b/DOCKER.md index 9eb5f32..7355ac7 100644 --- a/DOCKER.md +++ b/DOCKER.md @@ -6,6 +6,7 @@ Three Docker environments are available: - `Dockerfile.test` with `docker-compose.test.yml` - `Dockerfile.production` with `docker-compose.production.yml` - `docker-compose.pgmanage.yml` for a standalone pgManage container +- `docker-compose.portainer.production.yml` for a standalone production Portainer deployment behind Traefik ### Development @@ -118,6 +119,7 @@ Add an A record for every subdomain to your server's public IP before deploying | `rentaldrivego.ma` | marketplace and public site | | `api.rentaldrivego.ma` | API | | `pgmanage.rentaldrivego.ma` | pgManage (DB admin) | +| `portainer.rentaldrivego.ma` | Portainer | #### 2. Install Docker and clone the repo @@ -155,6 +157,7 @@ Open `.env.docker.production` and fill in every value. The minimum required secr Production now derives `DATABASE_URL` inside the app container from `POSTGRES_HOST`, `POSTGRES_PORT`, `POSTGRES_DB`, `POSTGRES_USER`, and `POSTGRES_PASSWORD` when `DATABASE_URL_FROM_POSTGRES=true`. That avoids Prisma auth failures when the database password contains reserved URL characters such as `@`, `:`, or `/`. 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. #### 4a. Configure the registry for CI deploys @@ -202,6 +205,7 @@ npm run docker:prod:start:dashboard npm run docker:prod:start:admin npm run docker:prod:start:frontends npm run docker:prod:start:pgmanage +npm run docker:prod:start:portainer npm run docker:prod:start:all ``` @@ -211,6 +215,20 @@ Docker will: Traefik automatically picks up the containers and provisions TLS certificates. Services are live at their `https://` URLs within ~30 seconds. +#### 6a. Deploy Portainer + +Portainer is deployed separately from the main app stack and reuses the shared `traefik-proxy` network managed by Traefik. + +```bash +npm run docker:prod:start:portainer +``` + +Equivalent raw Docker Compose command: + +```bash +docker compose -p rentaldrivego-portainer-prod --env-file .env.docker.production -f docker-compose.portainer.production.yml up -d +``` + 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. #### Updating after a code change @@ -345,6 +363,16 @@ pgManage is available at `https://pgmanage.rentaldrivego.ma`. To connect to the - **Username:** `postgres` - **Password:** value of `POSTGRES_PASSWORD` from `.env.docker.production` +#### Portainer + +Portainer is available at `https://portainer.rentaldrivego.ma`. + +Use this command to deploy or update it: + +```bash +npm run docker:prod:start:portainer +``` + ### Notes - The production image builds the whole monorepo once, then each service overrides its runtime command. diff --git a/package.json b/package.json index 3ec8aba..eea1f5e 100644 --- a/package.json +++ b/package.json @@ -21,6 +21,7 @@ "docker:prod:start:admin": "bash scripts/docker-prod-up-admin.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:portainer": "bash scripts/docker-prod-up-portainer.sh", "docker:prod:up": "docker compose -p rentaldrivego-prod --env-file .env.docker.production -f docker-compose.production.yml up --build -d", "docker:prod:down": "docker compose -p rentaldrivego-prod --env-file .env.docker.production -f docker-compose.production.yml down", "docker:prod:backup": "bash scripts/docker-prod-backup.sh", diff --git a/scripts/docker-prod-common.sh b/scripts/docker-prod-common.sh index 6a26879..eca332f 100755 --- a/scripts/docker-prod-common.sh +++ b/scripts/docker-prod-common.sh @@ -4,9 +4,11 @@ set -euo pipefail ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" PROD_COMPOSE_FILE="${ROOT_DIR}/docker-compose.production.yml" +PORTAINER_COMPOSE_FILE="${ROOT_DIR}/docker-compose.portainer.production.yml" TRAEFIK_COMPOSE_FILE="${ROOT_DIR}/traefik.yaml" ENV_FILE="${ROOT_DIR}/.env.docker.production" PROJECT_NAME="${DOCKER_PROD_PROJECT_NAME:-rentaldrivego-prod}" +PORTAINER_PROJECT_NAME="${DOCKER_PORTAINER_PROJECT_NAME:-rentaldrivego-portainer-prod}" TRAEFIK_NETWORK="${DOCKER_PROD_TRAEFIK_NETWORK:-traefik-proxy}" VOLUME_BACKUP_IMAGE="${DOCKER_VOLUME_BACKUP_IMAGE:-postgres:16-alpine}" @@ -29,6 +31,10 @@ prod_compose() { docker compose -p "${PROJECT_NAME}" --env-file "${ENV_FILE}" -f "${PROD_COMPOSE_FILE}" "$@" } +portainer_compose() { + docker compose -p "${PORTAINER_PROJECT_NAME}" --env-file "${ENV_FILE}" -f "${PORTAINER_COMPOSE_FILE}" "$@" +} + traefik_compose() { docker compose --env-file "${ENV_FILE}" -f "${TRAEFIK_COMPOSE_FILE}" "$@" } @@ -38,6 +44,12 @@ start_prod_services() { prod_compose up --build -d "$@" } +start_portainer() { + ensure_env_file + ensure_traefik_network + portainer_compose up -d +} + start_traefik() { ensure_env_file ensure_traefik_network diff --git a/scripts/docker-prod-up-portainer.sh b/scripts/docker-prod-up-portainer.sh new file mode 100644 index 0000000..36eff63 --- /dev/null +++ b/scripts/docker-prod-up-portainer.sh @@ -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_portainer + +echo "Portainer is starting."