Assist_Design/docker/prod-portainer/PORTAINER-GUIDE.md
barsa 87fa789fce Add Account and Auth API Services with Profile Management
- Introduced `accountService` for managing user profiles, including fetching and updating profile and address information.
- Created `auth.store` to handle client-side authentication state, including login, signup, and session management.
- Added centralized exports for authentication services in `auth/api/index.ts`.
- Implemented API services for billing and checkout functionalities, enhancing the overall service architecture.
- Established a new structure for service APIs, promoting better organization and maintainability across the portal features.
2025-12-29 18:19:27 +09:00

13 KiB

Complete Portainer Guide for Customer Portal

Table of Contents

  1. Creating a Stack in Portainer
  2. Repository vs Upload vs Web Editor
  3. Security Concerns & Best Practices
  4. Auto-Updating Images
  5. Recommended Setup for Production

Creating a Stack in Portainer

Step 1: Access Portainer

  1. Open Portainer UI (typically at https://your-server:9443 or via Plesk)
  2. Select your environment (usually "local" for Plesk)
  3. Go to Stacks in the left sidebar

Step 2: Create New Stack

Click "+ Add stack" button

You'll see three creation methods:

  • Web editor - Paste compose file directly
  • Upload - Upload a compose file
  • Repository - Pull from Git repository

Step 3: Configure the Stack

Name: customer-portal (lowercase, no spaces)

Compose content: Use one of the methods below

Environment variables: Add your configuration

Step 4: Deploy

Click "Deploy the stack"


Stack Creation Methods

Method 1: Web Editor (Simplest)

How:

  1. Select "Web editor"
  2. Paste your docker-compose.yml content
  3. Add environment variables manually or load from file

Pros:

  • Quick and simple
  • No external dependencies
  • Full control over content

Cons:

  • Manual updates required
  • No version control
  • Easy to make mistakes when editing

Best for: Quick testing, simple deployments


How:

  1. Select "Upload"
  2. Upload your docker-compose.yml file
  3. Optionally upload a .env file for environment variables

Pros:

  • Version control on your local machine
  • Can prepare and test locally
  • No external network dependencies
  • Works in air-gapped environments

Cons:

  • Manual upload for each update
  • Need to manage files locally

Best for: Production deployments with manual control


Method 3: Repository (Git Integration)

How:

  1. Select "Repository"
  2. Enter repository URL (GitHub, GitLab, Bitbucket, etc.)
  3. Specify branch and compose file path
  4. Add authentication if private repo

Example Configuration:

Repository URL: https://github.com/your-org/customer-portal
Reference: main
Compose path: docker/portainer/docker-compose.yml

For Private Repos:

  • Use a Personal Access Token (PAT) as password
  • Or use deploy keys

Pros:

  • Version controlled
  • Easy to update (just click "Pull and redeploy")
  • Team can review changes via PR
  • Audit trail of changes

Cons:

  • Requires network access to repo
  • Secrets in repo = security risk
  • Need to manage repo access tokens
  • Compose file changes require git push

Best for: Teams, CI/CD pipelines, frequent updates


📌 My Recommendation for Your Case

Use: Upload + Environment Variables in Portainer UI

Why:

  1. Your compose file rarely changes (it's just orchestration)
  2. Sensitive data stays in Portainer, not in Git
  3. Image updates are done via environment variables
  4. No external dependencies during deployment

Security Concerns

🔴 Critical Security Issues

1. Never Store Secrets in Git

# ❌ BAD - Secrets in compose file
environment:
  JWT_SECRET: "my-actual-secret-here"
  DATABASE_URL: "postgresql://user:password@db/prod"

# ✅ GOOD - Use environment variables
environment:
  JWT_SECRET: ${JWT_SECRET}
  DATABASE_URL: ${DATABASE_URL}

2. Never Store Secrets in Docker Images

# ❌ BAD - Secrets baked into image
ENV JWT_SECRET="my-secret"
COPY secrets/ /app/secrets/

# ✅ GOOD - Mount at runtime
# (secrets passed via env vars or volume mounts)

3. Portainer Access Control

⚠️ Portainer has full Docker access = root on the host

Best practices:
- Use strong passwords
- Enable 2FA if available
- Restrict network access to Portainer UI
- Use HTTPS only
- Create separate users with limited permissions

🟡 Medium Security Concerns

4. Environment Variables in Portainer

Portainer stores env vars in its database.
This is generally safe, but consider:

- Portainer database is at /data/portainer.db
- Anyone with Portainer admin = sees all secrets
- Backup files may contain secrets

Mitigation:
- Limit Portainer admin access
- Use Docker secrets for highly sensitive data
- Encrypt backups

5. Image Trust

⚠️ You're loading .tar files - verify their integrity

Best practice:
- Generate checksums when building
- Verify checksums before loading
- Use signed images if possible

Add to build script:

# Generate checksums
sha256sum portal-frontend.latest.tar > portal-frontend.latest.tar.sha256
sha256sum portal-backend.latest.tar > portal-backend.latest.tar.sha256

# Verify on server
sha256sum -c portal-frontend.latest.tar.sha256
sha256sum -c portal-backend.latest.tar.sha256

6. Network Exposure

# ❌ BAD - Database exposed to host
database:
  ports:
    - "5432:5432"  # Accessible from outside!

# ✅ GOOD - Internal network only
database:
  # No ports exposed - only accessible via portal-network
  networks:
    - portal-network

🟢 Good Security Practices (Already in Place)

Your current setup does these right:

  • Non-root users in containers
  • Health checks configured
  • Database/Redis not exposed externally
  • Secrets mounted as read-only volumes
  • Production error messages hide sensitive info

Auto-Updating Images

Watchtower automatically updates containers when new images are available.

# Add to your stack (if using registry)
watchtower:
  image: containrrr/watchtower
  volumes:
    - /var/run/docker.sock:/var/run/docker.sock
  environment:
    - WATCHTOWER_POLL_INTERVAL=300
    - WATCHTOWER_CLEANUP=true
  command: --include-stopped portal-frontend portal-backend

Why NOT recommended:

  • No control over when updates happen
  • No rollback mechanism
  • Can break production unexpectedly
  • Requires images in a registry (not .tar files)

We've disabled Watchtower in your compose:

labels:
  - "com.centurylinklabs.watchtower.enable=false"

Option 2: Portainer Webhooks (Semi-Automatic)

Portainer can expose a webhook URL that triggers stack redeployment.

Setup:

  1. Go to Stack → Settings
  2. Enable "Webhook"
  3. Copy the webhook URL

Trigger from CI/CD:

# In your GitHub Actions / GitLab CI
curl -X POST "https://your-portainer:9443/api/stacks/webhook/abc123"

Workflow:

Build Images → Push to Registry → Trigger Webhook → Portainer Redeploys

Pros:

  • Controlled updates
  • Integrated with CI/CD
  • Can add approval gates

Cons:

  • Requires images in a registry
  • Webhook URL is a secret
  • Limited rollback options

Since you're using .tar files (no registry), a manual update script is best:

# On your local machine after building:
./scripts/plesk/build-images.sh --tag v1.2.3

# Upload to server
scp portal-*.v1.2.3.tar user@server:/path/to/images/

# SSH and run update
ssh user@server "cd /path/to/portal && ./update-stack.sh v1.2.3"

Make it a one-liner:

# deploy.sh - Run locally
#!/bin/bash
TAG=$1
SERVER="user@your-server"
REMOTE_PATH="/var/www/vhosts/domain/portal"

# Build
./scripts/plesk/build-images.sh --tag "$TAG"

# Upload
scp portal-frontend.${TAG}.tar portal-backend.${TAG}.tar ${SERVER}:${REMOTE_PATH}/images/

# Deploy
ssh $SERVER "cd ${REMOTE_PATH} && ./update-stack.sh ${TAG}"

echo "✅ Deployed ${TAG}"

Option 4: Use a Container Registry (Most Professional)

If you want auto-updates, use a registry:

Free Options:

  • GitHub Container Registry (ghcr.io) - free for public repos
  • GitLab Container Registry - free
  • Docker Hub - 1 private repo free

Setup:

# Build and push
./scripts/plesk/build-images.sh --tag v1.2.3 --push ghcr.io/your-org

# Update compose to use registry
services:
  frontend:
    image: ghcr.io/your-org/portal-frontend:${TAG:-latest}

Then use Watchtower or webhooks for auto-updates.


For Your Current Situation (No Registry)

┌─────────────────┐    ┌──────────────────┐    ┌─────────────────┐
│  Local Dev      │    │   Plesk Server   │    │    Portainer    │
│                 │    │                  │    │                 │
│ 1. Build images │───▶│ 2. Load .tar     │───▶│ 3. Update stack │
│    with tag     │    │    files         │    │    env vars     │
│                 │    │                  │    │                 │
└─────────────────┘    └──────────────────┘    └─────────────────┘
       ▲                                              │
       │                                              ▼
       └──────────────────────────────────────────────┘
                    4. Verify & rollback if needed

Steps:

  1. Build: ./scripts/plesk/build-images.sh --tag 20241201-abc
  2. Upload: scp *.tar server:/path/images/
  3. Load: docker load -i *.tar
  4. Update: Change FRONTEND_IMAGE and BACKEND_IMAGE in Portainer
  5. Redeploy: Click "Update the stack" in Portainer

For Future (With Registry)

┌─────────────────┐    ┌──────────────────┐    ┌─────────────────┐
│   GitHub        │    │   GitHub         │    │    Portainer    │
│   (Code)        │───▶│   Actions        │───▶│   (Webhook)     │
│                 │    │   (Build & Push) │    │                 │
└─────────────────┘    └────────┬─────────┘    └────────┬────────┘
                                │                       │
                                ▼                       ▼
                       ┌────────────────┐      ┌────────────────┐
                       │   ghcr.io      │      │  Plesk Server  │
                       │   (Registry)   │◀─────│  (Pull Image)  │
                       └────────────────┘      └────────────────┘

Quick Reference: Portainer Stack Commands

Via Portainer UI

Action Steps
Create stack Stacks → Add stack → Configure → Deploy
Update stack Stacks → Select → Editor → Update
Change image Stacks → Select → Env vars → Change IMAGE → Update
View logs Stacks → Select → Container → Logs
Restart Stacks → Select → Container → Restart
Stop Stacks → Select → Stop
Delete Stacks → Select → Delete

Via CLI (on server)

# Navigate to stack directory
cd /path/to/portal

# View status
docker compose --env-file stack.env ps

# View logs
docker compose --env-file stack.env logs -f

# Restart
docker compose --env-file stack.env restart

# Update (after changing stack.env)
docker compose --env-file stack.env up -d

# Stop
docker compose --env-file stack.env down

# Stop and remove volumes (⚠️ DATA LOSS)
docker compose --env-file stack.env down -v

Summary

Aspect Recommendation
Stack creation Upload method (version control locally, no secrets in git)
Secrets management Portainer env vars or mounted secrets volume
Image updates Manual script for now, migrate to registry + webhook later
Auto-updates Not recommended for production; use controlled deployments
Rollback Keep previous image tags, update env vars to rollback