update production setup
This commit is contained in:
+5
-5
@@ -24,14 +24,14 @@ CORS_ORIGINS=http://localhost:3000,http://localhost:3001,http://localhost:3002,h
|
|||||||
|
|
||||||
# Email - disable Resend (placeholder), use SMTP instead
|
# Email - disable Resend (placeholder), use SMTP instead
|
||||||
RESEND_API_KEY=re_...
|
RESEND_API_KEY=re_...
|
||||||
EMAIL_FROM=moulay.elabidi@gmail.com
|
EMAIL_FROM=rentaldrivego@gmail.com
|
||||||
EMAIL_FROM_NAME=RentalDriveGo
|
EMAIL_FROM_NAME=RentalDriveGo
|
||||||
MAIL_HOST=smtp.gmail.com
|
MAIL_HOST=smtp.gmail.com
|
||||||
MAIL_PORT=587
|
MAIL_PORT=587
|
||||||
MAIL_SCHEME=smtp
|
MAIL_SCHEME=smtp
|
||||||
MAIL_USERNAME=moulay.elabidi@gmail.com
|
MAIL_USERNAME=rentaldrivego@gmail.com
|
||||||
MAIL_PASSWORD=seld rlvl bzuy npar
|
MAIL_PASSWORD=xmhg ibqy muoc rntc
|
||||||
MAIL_FROM_ADDRESS=moulay.elabidi@gmail.com
|
MAIL_FROM_ADDRESS=rentaldrivego@gmail.com
|
||||||
MAIL_FROM_NAME=RentalDriveGo
|
MAIL_FROM_NAME=RentalDriveGo
|
||||||
MAIL_REPLY_TO_ADDRESS=moulay.elabidi@gmail.com
|
MAIL_REPLY_TO_ADDRESS=rentaldrivego@gmail.com
|
||||||
MAIL_REPLY_TO_NAME=RentalDriveGo
|
MAIL_REPLY_TO_NAME=RentalDriveGo
|
||||||
|
|||||||
@@ -1,17 +1,66 @@
|
|||||||
|
# ── Traefik domains — used in docker-compose labels ──────────────────────────
|
||||||
|
ACME_EMAIL=admin@rentaldrivego.ma
|
||||||
|
MARKETPLACE_DOMAIN=rentaldrivego.ma
|
||||||
|
API_DOMAIN=api.rentaldrivego.ma
|
||||||
|
DASHBOARD_DOMAIN=dashboard.rentaldrivego.ma
|
||||||
|
ADMIN_DOMAIN=admin.rentaldrivego.ma
|
||||||
|
PUBLIC_SITE_DOMAIN=rentaldrivego.ma
|
||||||
|
PGMANAGE_DOMAIN=pgmanage.rentaldrivego.ma
|
||||||
|
|
||||||
DATABASE_URL=postgresql://postgres:change-me@postgres:5432/rentaldrivego
|
DATABASE_URL=postgresql://postgres:change-me@postgres:5432/rentaldrivego
|
||||||
REDIS_URL=redis://redis:6379
|
REDIS_URL=redis://redis:6379
|
||||||
|
|
||||||
|
# ── API ────────────────────────────────────────────────────────────────────────
|
||||||
API_PORT=4000
|
API_PORT=4000
|
||||||
API_URL=http://localhost:4000
|
# 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
|
||||||
NEXT_PUBLIC_API_URL=http://localhost:4000/api/v1
|
# Public-facing API URL visible to browsers — MUST be your real domain, not localhost
|
||||||
NEXT_PUBLIC_MARKETING_URL=http://localhost:3000
|
API_URL=https://api.rentaldrivego.ma
|
||||||
NEXT_PUBLIC_MARKETPLACE_URL=http://localhost:3000/explore
|
# Baked into Next.js bundles at build time — MUST be set before running `npm run build`
|
||||||
NEXT_PUBLIC_DASHBOARD_URL=http://localhost:3001
|
NEXT_PUBLIC_API_URL=https://api.rentaldrivego.ma/api/v1
|
||||||
NEXT_PUBLIC_ADMIN_URL=http://localhost:3002
|
|
||||||
NEXT_PUBLIC_PUBLIC_SITE_DOMAIN=localhost:3003
|
# ── Frontend public URLs ───────────────────────────────────────────────────────
|
||||||
DASHBOARD_URL=http://localhost:3001
|
NEXT_PUBLIC_MARKETING_URL=https://rentaldrivego.ma
|
||||||
|
NEXT_PUBLIC_MARKETPLACE_URL=https://rentaldrivego.ma/explore
|
||||||
|
NEXT_PUBLIC_DASHBOARD_URL=https://dashboard.rentaldrivego.ma
|
||||||
|
NEXT_PUBLIC_ADMIN_URL=https://admin.rentaldrivego.ma
|
||||||
|
NEXT_PUBLIC_PUBLIC_SITE_DOMAIN=rentaldrivego.ma
|
||||||
|
DASHBOARD_URL=https://dashboard.rentaldrivego.ma
|
||||||
|
|
||||||
|
# ── CORS ──────────────────────────────────────────────────────────────────────
|
||||||
|
# Comma-separated list of allowed browser origins. REQUIRED in production.
|
||||||
|
CORS_ORIGINS=https://rentaldrivego.ma,https://dashboard.rentaldrivego.ma,https://admin.rentaldrivego.ma
|
||||||
|
|
||||||
|
# ── Auth ──────────────────────────────────────────────────────────────────────
|
||||||
JWT_SECRET=replace-with-a-long-random-secret
|
JWT_SECRET=replace-with-a-long-random-secret
|
||||||
JWT_EXPIRY=8h
|
JWT_EXPIRY=8h
|
||||||
RENTER_JWT_EXPIRY=7d
|
RENTER_JWT_EXPIRY=7d
|
||||||
|
|
||||||
|
# ── Database ──────────────────────────────────────────────────────────────────
|
||||||
POSTGRES_PASSWORD=replace-with-a-real-password
|
POSTGRES_PASSWORD=replace-with-a-real-password
|
||||||
NODE_ENV=production
|
NODE_ENV=production
|
||||||
|
|
||||||
|
# ── Email (choose one: Resend API or SMTP) ────────────────────────────────────
|
||||||
|
# Option A — Resend
|
||||||
|
RESEND_API_KEY=re_your_resend_api_key
|
||||||
|
EMAIL_FROM=noreply@rentaldrivego.ma
|
||||||
|
EMAIL_FROM_NAME=RentalDriveGo
|
||||||
|
# Option B — SMTP
|
||||||
|
# MAIL_HOST=smtp.rentaldrivego.ma
|
||||||
|
# MAIL_PORT=587
|
||||||
|
# MAIL_USERNAME=smtp-user
|
||||||
|
# MAIL_PASSWORD=smtp-password
|
||||||
|
# MAIL_SCHEME=smtp
|
||||||
|
# MAIL_FROM_ADDRESS=noreply@rentaldrivego.ma
|
||||||
|
# MAIL_FROM_NAME=RentalDriveGo
|
||||||
|
|
||||||
|
# ── Firebase push notifications (optional) ────────────────────────────────────
|
||||||
|
# FIREBASE_PROJECT_ID=your-firebase-project-id
|
||||||
|
# FIREBASE_CLIENT_EMAIL=firebase-adminsdk@your-project.iam.gserviceaccount.com
|
||||||
|
# FIREBASE_PRIVATE_KEY="-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----\n"
|
||||||
|
|
||||||
|
# ── Twilio SMS / WhatsApp (optional) ─────────────────────────────────────────
|
||||||
|
# TWILIO_ACCOUNT_SID=ACxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||||
|
# TWILIO_AUTH_TOKEN=your_auth_token
|
||||||
|
# TWILIO_PHONE_NUMBER=+1234567890
|
||||||
|
# TWILIO_WHATSAPP_NUMBER=+1234567890
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
ACME_EMAIL=rentaldrivego@gmail.com
|
||||||
|
TRAEFIK_DOMAIN=rentaldrivego.ma
|
||||||
|
TRAEFIK_DOMAIN_WWW=www.rentaldrivego.ma
|
||||||
@@ -5,7 +5,7 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "node --env-file=../../.env.local ../../node_modules/.bin/ts-node-dev --respawn --transpile-only src/index.ts",
|
"dev": "node --env-file=../../.env.local ../../node_modules/.bin/ts-node-dev --respawn --transpile-only src/index.ts",
|
||||||
"build": "tsc",
|
"build": "tsc",
|
||||||
"start": "node --env-file=../../.env.local dist/index.js",
|
"start": "node dist/index.js",
|
||||||
"type-check": "tsc --noEmit"
|
"type-check": "tsc --noEmit"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|||||||
@@ -32,6 +32,12 @@ import paymentsRouter from './routes/payments'
|
|||||||
|
|
||||||
const app = express()
|
const app = express()
|
||||||
const server = http.createServer(app)
|
const server = http.createServer(app)
|
||||||
|
|
||||||
|
// Trust the first hop from a reverse proxy so req.ip and rate-limiting
|
||||||
|
// use the real client IP (from X-Forwarded-For) rather than the proxy's IP.
|
||||||
|
if (process.env.NODE_ENV === 'production') {
|
||||||
|
app.set('trust proxy', 1)
|
||||||
|
}
|
||||||
const v1 = '/api/v1'
|
const v1 = '/api/v1'
|
||||||
const defaultCorsOrigins = [
|
const defaultCorsOrigins = [
|
||||||
'http://localhost:3000',
|
'http://localhost:3000',
|
||||||
|
|||||||
@@ -1,15 +1,13 @@
|
|||||||
import rateLimit, { ipKeyGenerator } from 'express-rate-limit'
|
import rateLimit, { ipKeyGenerator } from 'express-rate-limit'
|
||||||
|
import type { Request } from 'express'
|
||||||
|
|
||||||
const trustProxy = process.env.NODE_ENV === 'production'
|
// req.ip is already the real client IP when app.set('trust proxy', 1) is configured
|
||||||
const getClientIpKey = (req: Parameters<typeof ipKeyGenerator>[0]) =>
|
const getClientIpKey = (req: Request) => ipKeyGenerator(req.ip ?? '')
|
||||||
trustProxy
|
|
||||||
? ipKeyGenerator((req.headers['x-forwarded-for'] as string)?.split(',')[0]?.trim() ?? req.ip ?? '')
|
|
||||||
: ipKeyGenerator(req.ip ?? '')
|
|
||||||
|
|
||||||
// Strict limiter for auth endpoints — prevents brute-force and credential stuffing
|
// Strict limiter for auth endpoints — prevents brute-force and credential stuffing
|
||||||
export const authLimiter = rateLimit({
|
export const authLimiter = rateLimit({
|
||||||
windowMs: 15 * 60 * 1000, // 15 minutes
|
windowMs: 15 * 60 * 1000, // 15 minutes
|
||||||
max: 960,
|
max: 20,
|
||||||
standardHeaders: 'draft-7',
|
standardHeaders: 'draft-7',
|
||||||
legacyHeaders: false,
|
legacyHeaders: false,
|
||||||
skipSuccessfulRequests: false,
|
skipSuccessfulRequests: false,
|
||||||
@@ -20,7 +18,7 @@ export const authLimiter = rateLimit({
|
|||||||
// Standard limiter for general authenticated API endpoints
|
// Standard limiter for general authenticated API endpoints
|
||||||
export const apiLimiter = rateLimit({
|
export const apiLimiter = rateLimit({
|
||||||
windowMs: 60 * 1000, // 1 minute
|
windowMs: 60 * 1000, // 1 minute
|
||||||
max: 960,
|
max: 120,
|
||||||
standardHeaders: 'draft-7',
|
standardHeaders: 'draft-7',
|
||||||
legacyHeaders: false,
|
legacyHeaders: false,
|
||||||
keyGenerator: (req) => {
|
keyGenerator: (req) => {
|
||||||
@@ -32,10 +30,10 @@ export const apiLimiter = rateLimit({
|
|||||||
message: { error: 'too_many_requests', message: 'Rate limit exceeded', statusCode: 429 },
|
message: { error: 'too_many_requests', message: 'Rate limit exceeded', statusCode: 429 },
|
||||||
})
|
})
|
||||||
|
|
||||||
// Tight limiter for public marketplace and site endpoints (no auth)
|
// Limiter for public marketplace and site endpoints (no auth)
|
||||||
export const publicLimiter = rateLimit({
|
export const publicLimiter = rateLimit({
|
||||||
windowMs: 60 * 1000,
|
windowMs: 60 * 1000,
|
||||||
max: 960,
|
max: 60,
|
||||||
standardHeaders: 'draft-7',
|
standardHeaders: 'draft-7',
|
||||||
legacyHeaders: false,
|
legacyHeaders: false,
|
||||||
keyGenerator: (req) => getClientIpKey(req),
|
keyGenerator: (req) => getClientIpKey(req),
|
||||||
@@ -45,7 +43,7 @@ export const publicLimiter = rateLimit({
|
|||||||
// Tight limiter for admin endpoints
|
// Tight limiter for admin endpoints
|
||||||
export const adminLimiter = rateLimit({
|
export const adminLimiter = rateLimit({
|
||||||
windowMs: 15 * 60 * 1000,
|
windowMs: 15 * 60 * 1000,
|
||||||
max: 960,
|
max: 100,
|
||||||
standardHeaders: 'draft-7',
|
standardHeaders: 'draft-7',
|
||||||
legacyHeaders: false,
|
legacyHeaders: false,
|
||||||
keyGenerator: (req) => getClientIpKey(req),
|
keyGenerator: (req) => getClientIpKey(req),
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ services:
|
|||||||
postgres:
|
postgres:
|
||||||
image: postgres:16-alpine
|
image: postgres:16-alpine
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
networks:
|
||||||
|
- internal
|
||||||
environment:
|
environment:
|
||||||
POSTGRES_DB: rentaldrivego
|
POSTGRES_DB: rentaldrivego
|
||||||
POSTGRES_USER: postgres
|
POSTGRES_USER: postgres
|
||||||
@@ -17,6 +19,8 @@ services:
|
|||||||
redis:
|
redis:
|
||||||
image: redis:7-alpine
|
image: redis:7-alpine
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
networks:
|
||||||
|
- internal
|
||||||
volumes:
|
volumes:
|
||||||
- redis_prod_data:/data
|
- redis_prod_data:/data
|
||||||
|
|
||||||
@@ -25,6 +29,8 @@ services:
|
|||||||
context: .
|
context: .
|
||||||
dockerfile: Dockerfile.production
|
dockerfile: Dockerfile.production
|
||||||
command: ["sh", "-c", "npm run db:deploy"]
|
command: ["sh", "-c", "npm run db:deploy"]
|
||||||
|
networks:
|
||||||
|
- internal
|
||||||
depends_on:
|
depends_on:
|
||||||
postgres:
|
postgres:
|
||||||
condition: service_healthy
|
condition: service_healthy
|
||||||
@@ -37,6 +43,9 @@ services:
|
|||||||
context: .
|
context: .
|
||||||
dockerfile: Dockerfile.production
|
dockerfile: Dockerfile.production
|
||||||
command: ["npm", "run", "start", "--workspace", "@rentaldrivego/api"]
|
command: ["npm", "run", "start", "--workspace", "@rentaldrivego/api"]
|
||||||
|
networks:
|
||||||
|
- internal
|
||||||
|
- traefik-proxy
|
||||||
depends_on:
|
depends_on:
|
||||||
postgres:
|
postgres:
|
||||||
condition: service_healthy
|
condition: service_healthy
|
||||||
@@ -46,62 +55,122 @@ services:
|
|||||||
condition: service_completed_successfully
|
condition: service_completed_successfully
|
||||||
env_file:
|
env_file:
|
||||||
- .env.docker.production
|
- .env.docker.production
|
||||||
ports:
|
|
||||||
- "4000:4000"
|
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
labels:
|
||||||
|
- traefik.enable=true
|
||||||
|
- traefik.http.routers.api.rule=Host(`${API_DOMAIN}`)
|
||||||
|
- traefik.http.routers.api.entrypoints=websecure
|
||||||
|
- traefik.http.routers.api.tls.certresolver=letsencrypt
|
||||||
|
- traefik.http.services.api.loadbalancer.server.port=4000
|
||||||
|
|
||||||
marketplace:
|
marketplace:
|
||||||
build:
|
build:
|
||||||
context: .
|
context: .
|
||||||
dockerfile: Dockerfile.production
|
dockerfile: Dockerfile.production
|
||||||
command: ["npm", "run", "start", "--workspace", "@rentaldrivego/marketplace"]
|
command: ["npm", "run", "start", "--workspace", "@rentaldrivego/marketplace"]
|
||||||
|
networks:
|
||||||
|
- internal
|
||||||
|
- traefik-proxy
|
||||||
depends_on:
|
depends_on:
|
||||||
- api
|
- api
|
||||||
env_file:
|
env_file:
|
||||||
- .env.docker.production
|
- .env.docker.production
|
||||||
ports:
|
|
||||||
- "3000:3000"
|
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
labels:
|
||||||
|
- traefik.enable=true
|
||||||
|
- traefik.http.routers.marketplace.rule=Host(`${MARKETPLACE_DOMAIN}`)
|
||||||
|
- traefik.http.routers.marketplace.entrypoints=websecure
|
||||||
|
- traefik.http.routers.marketplace.tls.certresolver=letsencrypt
|
||||||
|
- traefik.http.services.marketplace.loadbalancer.server.port=3000
|
||||||
|
|
||||||
dashboard:
|
dashboard:
|
||||||
build:
|
build:
|
||||||
context: .
|
context: .
|
||||||
dockerfile: Dockerfile.production
|
dockerfile: Dockerfile.production
|
||||||
command: ["npm", "run", "start", "--workspace", "@rentaldrivego/dashboard"]
|
command: ["npm", "run", "start", "--workspace", "@rentaldrivego/dashboard"]
|
||||||
|
networks:
|
||||||
|
- internal
|
||||||
|
- traefik-proxy
|
||||||
depends_on:
|
depends_on:
|
||||||
- api
|
- api
|
||||||
env_file:
|
env_file:
|
||||||
- .env.docker.production
|
- .env.docker.production
|
||||||
ports:
|
|
||||||
- "3001:3001"
|
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
labels:
|
||||||
|
- traefik.enable=true
|
||||||
|
- traefik.http.routers.dashboard.rule=Host(`${DASHBOARD_DOMAIN}`)
|
||||||
|
- traefik.http.routers.dashboard.entrypoints=websecure
|
||||||
|
- traefik.http.routers.dashboard.tls.certresolver=letsencrypt
|
||||||
|
- traefik.http.services.dashboard.loadbalancer.server.port=3001
|
||||||
|
|
||||||
admin:
|
admin:
|
||||||
build:
|
build:
|
||||||
context: .
|
context: .
|
||||||
dockerfile: Dockerfile.production
|
dockerfile: Dockerfile.production
|
||||||
command: ["npm", "run", "start", "--workspace", "@rentaldrivego/admin"]
|
command: ["npm", "run", "start", "--workspace", "@rentaldrivego/admin"]
|
||||||
|
networks:
|
||||||
|
- internal
|
||||||
|
- traefik-proxy
|
||||||
depends_on:
|
depends_on:
|
||||||
- api
|
- api
|
||||||
env_file:
|
env_file:
|
||||||
- .env.docker.production
|
- .env.docker.production
|
||||||
ports:
|
|
||||||
- "3002:3002"
|
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
labels:
|
||||||
|
- traefik.enable=true
|
||||||
|
- traefik.http.routers.admin.rule=Host(`${ADMIN_DOMAIN}`)
|
||||||
|
- traefik.http.routers.admin.entrypoints=websecure
|
||||||
|
- traefik.http.routers.admin.tls.certresolver=letsencrypt
|
||||||
|
- traefik.http.services.admin.loadbalancer.server.port=3002
|
||||||
|
|
||||||
public-site:
|
public-site:
|
||||||
build:
|
build:
|
||||||
context: .
|
context: .
|
||||||
dockerfile: Dockerfile.production
|
dockerfile: Dockerfile.production
|
||||||
command: ["npm", "run", "start", "--workspace", "@rentaldrivego/public-site"]
|
command: ["npm", "run", "start", "--workspace", "@rentaldrivego/public-site"]
|
||||||
|
networks:
|
||||||
|
- internal
|
||||||
|
- traefik-proxy
|
||||||
depends_on:
|
depends_on:
|
||||||
- api
|
- api
|
||||||
env_file:
|
env_file:
|
||||||
- .env.docker.production
|
- .env.docker.production
|
||||||
ports:
|
|
||||||
- "3003:3003"
|
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
labels:
|
||||||
|
- traefik.enable=true
|
||||||
|
- traefik.http.routers.public-site.rule=Host(`${PUBLIC_SITE_DOMAIN}`)
|
||||||
|
- traefik.http.routers.public-site.entrypoints=websecure
|
||||||
|
- traefik.http.routers.public-site.tls.certresolver=letsencrypt
|
||||||
|
- traefik.http.services.public-site.loadbalancer.server.port=3003
|
||||||
|
|
||||||
|
pgmanage:
|
||||||
|
image: cmdpromptinc/pgmanage-enterprise:latest
|
||||||
|
restart: unless-stopped
|
||||||
|
user: "0:0"
|
||||||
|
networks:
|
||||||
|
- internal
|
||||||
|
- traefik-proxy
|
||||||
|
depends_on:
|
||||||
|
postgres:
|
||||||
|
condition: service_healthy
|
||||||
|
environment:
|
||||||
|
PGMANAGE_LISTEN_PORT: "8000"
|
||||||
|
volumes:
|
||||||
|
- pgmanage_prod_data:/appdata
|
||||||
|
labels:
|
||||||
|
- traefik.enable=true
|
||||||
|
- traefik.http.routers.pgmanage.rule=Host(`${PGMANAGE_DOMAIN}`)
|
||||||
|
- traefik.http.routers.pgmanage.entrypoints=websecure
|
||||||
|
- traefik.http.routers.pgmanage.tls.certresolver=letsencrypt
|
||||||
|
- traefik.http.services.pgmanage.loadbalancer.server.port=8000
|
||||||
|
|
||||||
|
networks:
|
||||||
|
internal:
|
||||||
|
driver: bridge
|
||||||
|
traefik-proxy:
|
||||||
|
external: true
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
postgres_prod_data:
|
postgres_prod_data:
|
||||||
redis_prod_data:
|
redis_prod_data:
|
||||||
|
pgmanage_prod_data:
|
||||||
|
|||||||
@@ -0,0 +1,32 @@
|
|||||||
|
services:
|
||||||
|
traefik:
|
||||||
|
image: traefik:latest
|
||||||
|
restart: unless-stopped
|
||||||
|
command:
|
||||||
|
- --api.dashboard=false
|
||||||
|
- --api.insecure=false
|
||||||
|
- --providers.docker=true
|
||||||
|
- --providers.docker.exposedbydefault=false
|
||||||
|
- --entrypoints.web.address=:80
|
||||||
|
- --entrypoints.websecure.address=:443
|
||||||
|
- --certificatesresolvers.letsencrypt.acme.tlschallenge=true
|
||||||
|
- --certificatesresolvers.letsencrypt.acme.email=${ACME_EMAIL}
|
||||||
|
- --certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json
|
||||||
|
- --entrypoints.web.http.redirections.entrypoint.to=websecure
|
||||||
|
- --entrypoints.web.http.redirections.entrypoint.scheme=https
|
||||||
|
- --providers.docker.network=traefik-proxy
|
||||||
|
ports:
|
||||||
|
- "80:80"
|
||||||
|
- "443:443"
|
||||||
|
networks:
|
||||||
|
- traefik-proxy
|
||||||
|
volumes:
|
||||||
|
- traefik-letsencrypt:/letsencrypt
|
||||||
|
- /var/run/docker.sock:/var/run/docker.sock:ro
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
traefik-letsencrypt:
|
||||||
|
|
||||||
|
networks:
|
||||||
|
traefik-proxy:
|
||||||
|
external: true
|
||||||
Reference in New Issue
Block a user