- Added .env.development to .gitignore for better environment management. - Introduced new dev script in package.json for streamlined application development. - Updated Prisma migration commands in docker-entrypoint.sh for improved schema handling. - Enhanced logging configuration in logging.module.ts to support pretty logs based on environment. - Refactored app.config.ts to prioritize environment file loading for better configuration management. - Removed outdated test files and configurations to clean up the project structure.
12 KiB
Complete Portainer Guide for Customer Portal
Table of Contents
- Creating a Stack in Portainer
- Repository vs Upload vs Web Editor
- Security Concerns & Best Practices
- Auto-Updating Images
- Recommended Setup for Production
Creating a Stack in Portainer
Step 1: Access Portainer
- Open Portainer UI (typically at
https://your-server:9443or via Plesk) - Select your environment (usually "local" for Plesk)
- 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:
- Select "Web editor"
- Paste your
docker-compose.ymlcontent - 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
Method 2: Upload (Recommended for Your Case)
How:
- Select "Upload"
- Upload your
docker-compose.ymlfile - Optionally upload a
.envfile 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:
- Select "Repository"
- Enter repository URL (GitHub, GitLab, Bitbucket, etc.)
- Specify branch and compose file path
- 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:
- Your compose file rarely changes (it's just orchestration)
- Sensitive data stays in Portainer, not in Git
- Image updates are done via environment variables
- 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
Option 1: Watchtower (NOT Recommended for Production)
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:
- Go to Stack → Settings
- Enable "Webhook"
- 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
Option 3: Manual Script (Recommended for Your Case) ✅
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.
Recommended Production Setup
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:
- Build:
./scripts/plesk/build-images.sh --tag 20241201-abc - Upload:
scp *.tar server:/path/images/ - Load:
docker load -i *.tar - Update: Change
FRONTEND_IMAGEandBACKEND_IMAGEin Portainer - 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 |