~/
โโโ dtstack-pg/
โ โโโ docker-compose.yml
โ โโโ .env
โโโ dtstack-cb/ โโโ docker-compose.yml โโโ ...
~/
โโโ dtstack-pg/
โ โโโ docker-compose.yml
โ โโโ .env
โโโ dtstack-cb/ โโโ docker-compose.yml โโโ ...
~/
โโโ dtstack-pg/
โ โโโ docker-compose.yml
โ โโโ .env
โโโ dtstack-cb/ โโโ docker-compose.yml โโโ ...
$ mkdir -p cloudstack && cd cloudstack
$ mkdir -p cloudstack && cd cloudstack
$ mkdir -p cloudstack && cd cloudstack
services: postgres: container_name: dtstack-pg image: postgres:17 environment: POSTGRES_PASSWORD: ${PG_PASSWORD} POSTGRES_DB: ${PG_DATABASE} volumes: - pgdata:/var/lib/postgresql/data networks: - dtstack cloudbeaver: container_name: dtstack-cb image: dbeaver/cloudbeaver:latest ports: - "8978:8978" volumes: - cbdata:/opt/cloudbeaver/workspace networks: - dtstack volumes: pgdata: cbdata: networks: dtstack: driver: bridge
services: postgres: container_name: dtstack-pg image: postgres:17 environment: POSTGRES_PASSWORD: ${PG_PASSWORD} POSTGRES_DB: ${PG_DATABASE} volumes: - pgdata:/var/lib/postgresql/data networks: - dtstack cloudbeaver: container_name: dtstack-cb image: dbeaver/cloudbeaver:latest ports: - "8978:8978" volumes: - cbdata:/opt/cloudbeaver/workspace networks: - dtstack volumes: pgdata: cbdata: networks: dtstack: driver: bridge
services: postgres: container_name: dtstack-pg image: postgres:17 environment: POSTGRES_PASSWORD: ${PG_PASSWORD} POSTGRES_DB: ${PG_DATABASE} volumes: - pgdata:/var/lib/postgresql/data networks: - dtstack cloudbeaver: container_name: dtstack-cb image: dbeaver/cloudbeaver:latest ports: - "8978:8978" volumes: - cbdata:/opt/cloudbeaver/workspace networks: - dtstack volumes: pgdata: cbdata: networks: dtstack: driver: bridge
$ cat > .env << EOF
PG_PASSWORD=docker
PG_DATABASE=testdb
EOF
$ cat > .env << EOF
PG_PASSWORD=docker
PG_DATABASE=testdb
EOF
$ cat > .env << EOF
PG_PASSWORD=docker
PG_DATABASE=testdb
EOF
$ docker compose up -d
$ docker compose up -d
$ docker compose up -d
[+] up 32/32 โ Image postgres:17 Pulled โ Image dbeaver/cloudbeaver:latest Pulled โ Network cloudstack_dtstack Created โ Volume cloudstack_pgdata Created โ Volume cloudstack_cbdata Created โ Container dtstack-cb Started โ Container dtstack-pg Started
[+] up 32/32 โ Image postgres:17 Pulled โ Image dbeaver/cloudbeaver:latest Pulled โ Network cloudstack_dtstack Created โ Volume cloudstack_pgdata Created โ Volume cloudstack_cbdata Created โ Container dtstack-cb Started โ Container dtstack-pg Started
[+] up 32/32 โ Image postgres:17 Pulled โ Image dbeaver/cloudbeaver:latest Pulled โ Network cloudstack_dtstack Created โ Volume cloudstack_pgdata Created โ Volume cloudstack_cbdata Created โ Container dtstack-cb Started โ Container dtstack-pg Started
$ docker compose ps
NAME IMAGE COMMAND SERVICE CREATED STATUS PORTS
dtstack-cb dbeaver/cloudbeaver:latest "./launch-product.sh" cloudbeaver 5 min ago Up 5 min 0.0.0.0:8978->8978/tcp
dtstack-pg postgres:17 "docker-entrypoint.sโฆ" postgres 5 min ago Up 5 min 5432/tcp
$ docker compose ps
NAME IMAGE COMMAND SERVICE CREATED STATUS PORTS
dtstack-cb dbeaver/cloudbeaver:latest "./launch-product.sh" cloudbeaver 5 min ago Up 5 min 0.0.0.0:8978->8978/tcp
dtstack-pg postgres:17 "docker-entrypoint.sโฆ" postgres 5 min ago Up 5 min 5432/tcp
$ docker compose ps
NAME IMAGE COMMAND SERVICE CREATED STATUS PORTS
dtstack-cb dbeaver/cloudbeaver:latest "./launch-product.sh" cloudbeaver 5 min ago Up 5 min 0.0.0.0:8978->8978/tcp
dtstack-pg postgres:17 "docker-entrypoint.sโฆ" postgres 5 min ago Up 5 min 5432/tcp
$ docker network inspect cloudstack_dtstack
$ docker network inspect cloudstack_dtstack
$ docker network inspect cloudstack_dtstack
"Containers": { "abc123...": { "Name": "dtstack-pg", "IPv4Address": "172.19.0.2/16" }, "def456...": { "Name": "dtstack-cb", "IPv4Address": "172.19.0.3/16" }
}
"Containers": { "abc123...": { "Name": "dtstack-pg", "IPv4Address": "172.19.0.2/16" }, "def456...": { "Name": "dtstack-cb", "IPv4Address": "172.19.0.3/16" }
}
"Containers": { "abc123...": { "Name": "dtstack-pg", "IPv4Address": "172.19.0.2/16" }, "def456...": { "Name": "dtstack-cb", "IPv4Address": "172.19.0.3/16" }
}
$ docker compose down
$ docker compose down
$ docker compose down
[+] down 3/3 โ Container dtstack-cb Removed โ Container dtstack-pg Removed โ Network cloudstack_dtstack Removed
[+] down 3/3 โ Container dtstack-cb Removed โ Container dtstack-pg Removed โ Network cloudstack_dtstack Removed
[+] down 3/3 โ Container dtstack-cb Removed โ Container dtstack-pg Removed โ Network cloudstack_dtstack Removed
$ docker volume ls | grep cloudstack
$ docker volume ls | grep cloudstack
$ docker volume ls | grep cloudstack
local cloudstack_pgdata
local cloudstack_cbdata
local cloudstack_pgdata
local cloudstack_cbdata
local cloudstack_pgdata
local cloudstack_cbdata
$ docker compose up -d
$ docker compose up -d
$ docker compose up -d
$ docker compose down --volumes
$ docker compose down --volumes
$ docker compose down --volumes
[+] down 5/5 โ Container dtstack-pg Removed โ Container dtstack-cb Removed โ Volume cloudstack_cbdata Removed โ Volume cloudstack_pgdata Removed โ Network cloudstack_dtstack Removed
[+] down 5/5 โ Container dtstack-pg Removed โ Container dtstack-cb Removed โ Volume cloudstack_cbdata Removed โ Volume cloudstack_pgdata Removed โ Network cloudstack_dtstack Removed
[+] down 5/5 โ Container dtstack-pg Removed โ Container dtstack-cb Removed โ Volume cloudstack_cbdata Removed โ Volume cloudstack_pgdata Removed โ Network cloudstack_dtstack Removed
$ mkdir -p nextcloud && cd nextcloud
$ mkdir -p nextcloud && cd nextcloud
$ mkdir -p nextcloud && cd nextcloud
services: db: container_name: nc-db image: mariadb:11 environment: MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD} MYSQL_DATABASE: ${MYSQL_DATABASE} MYSQL_USER: ${MYSQL_USER} MYSQL_PASSWORD: ${MYSQL_PASSWORD} volumes: - dbdata:/var/lib/mysql networks: - nextcloud redis: container_name: nc-redis image: redis:8.6 volumes: - redisdata:/data networks: - nextcloud php: container_name: nc-php image: nextcloud:fpm volumes: - ./html:/var/www/html networks: - nextcloud nginx: container_name: nc-nginx image: nginx:latest ports: - "8080:80" volumes: - ./html:/var/www/html - ./nginx.conf:/etc/nginx/conf.d/default.conf networks: - nextcloud volumes: dbdata: redisdata: networks: nextcloud: driver: bridge
services: db: container_name: nc-db image: mariadb:11 environment: MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD} MYSQL_DATABASE: ${MYSQL_DATABASE} MYSQL_USER: ${MYSQL_USER} MYSQL_PASSWORD: ${MYSQL_PASSWORD} volumes: - dbdata:/var/lib/mysql networks: - nextcloud redis: container_name: nc-redis image: redis:8.6 volumes: - redisdata:/data networks: - nextcloud php: container_name: nc-php image: nextcloud:fpm volumes: - ./html:/var/www/html networks: - nextcloud nginx: container_name: nc-nginx image: nginx:latest ports: - "8080:80" volumes: - ./html:/var/www/html - ./nginx.conf:/etc/nginx/conf.d/default.conf networks: - nextcloud volumes: dbdata: redisdata: networks: nextcloud: driver: bridge
services: db: container_name: nc-db image: mariadb:11 environment: MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD} MYSQL_DATABASE: ${MYSQL_DATABASE} MYSQL_USER: ${MYSQL_USER} MYSQL_PASSWORD: ${MYSQL_PASSWORD} volumes: - dbdata:/var/lib/mysql networks: - nextcloud redis: container_name: nc-redis image: redis:8.6 volumes: - redisdata:/data networks: - nextcloud php: container_name: nc-php image: nextcloud:fpm volumes: - ./html:/var/www/html networks: - nextcloud nginx: container_name: nc-nginx image: nginx:latest ports: - "8080:80" volumes: - ./html:/var/www/html - ./nginx.conf:/etc/nginx/conf.d/default.conf networks: - nextcloud volumes: dbdata: redisdata: networks: nextcloud: driver: bridge
$ cat > .env << EOF
MYSQL_ROOT_PASSWORD=nextcloud
MYSQL_DATABASE=nextcloud
MYSQL_USER=nextcloud
MYSQL_PASSWORD=nextcloud
EOF
$ cat > .env << EOF
MYSQL_ROOT_PASSWORD=nextcloud
MYSQL_DATABASE=nextcloud
MYSQL_USER=nextcloud
MYSQL_PASSWORD=nextcloud
EOF
$ cat > .env << EOF
MYSQL_ROOT_PASSWORD=nextcloud
MYSQL_DATABASE=nextcloud
MYSQL_USER=nextcloud
MYSQL_PASSWORD=nextcloud
EOF
server { listen 80; server_name localhost; root /var/www/html; index index.php; location / { try_files $uri $uri/ /index.php?$query_string; } location ~ \.php$ { fastcgi_pass php:9000; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; include fastcgi_params; }
}
server { listen 80; server_name localhost; root /var/www/html; index index.php; location / { try_files $uri $uri/ /index.php?$query_string; } location ~ \.php$ { fastcgi_pass php:9000; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; include fastcgi_params; }
}
server { listen 80; server_name localhost; root /var/www/html; index index.php; location / { try_files $uri $uri/ /index.php?$query_string; } location ~ \.php$ { fastcgi_pass php:9000; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; include fastcgi_params; }
}
$ docker compose up -d
$ docker compose up -d
$ docker compose up -d
[+] up 19/19 โ Image nextcloud:fpm Pulled โ Image nginx:latest Pulled โ Image mariadb:11 Pulled โ Image redis:8.6 Pulled โ Network nextcloud_nextcloud Created โ Volume nextcloud_dbdata Created โ Volume nextcloud_redisdata Created โ Container nc-nginx Started โ Container nc-db Started โ Container nc-redis Started โ Container nc-php Started
[+] up 19/19 โ Image nextcloud:fpm Pulled โ Image nginx:latest Pulled โ Image mariadb:11 Pulled โ Image redis:8.6 Pulled โ Network nextcloud_nextcloud Created โ Volume nextcloud_dbdata Created โ Volume nextcloud_redisdata Created โ Container nc-nginx Started โ Container nc-db Started โ Container nc-redis Started โ Container nc-php Started
[+] up 19/19 โ Image nextcloud:fpm Pulled โ Image nginx:latest Pulled โ Image mariadb:11 Pulled โ Image redis:8.6 Pulled โ Network nextcloud_nextcloud Created โ Volume nextcloud_dbdata Created โ Volume nextcloud_redisdata Created โ Container nc-nginx Started โ Container nc-db Started โ Container nc-redis Started โ Container nc-php Started
$ docker compose ps
NAME IMAGE COMMAND SERVICE CREATED STATUS PORTS
nc-db mariadb:11 "docker-entrypoint.sโฆ" db 5 min ago Up 5 min 3306/tcp
nc-redis redis:8.6 "docker-entrypoint.sโฆ" redis 5 min ago Up 5 min 6379/tcp
nc-php nextcloud:fpm "docker-entrypoint.sโฆ" php 5 min ago Up 5 min 9000/tcp
nc-nginx nginx:latest "/docker-entrypoint.โฆ" nginx 5 min ago Up 5 min 0.0.0.0:8080->80/tcp
$ docker compose ps
NAME IMAGE COMMAND SERVICE CREATED STATUS PORTS
nc-db mariadb:11 "docker-entrypoint.sโฆ" db 5 min ago Up 5 min 3306/tcp
nc-redis redis:8.6 "docker-entrypoint.sโฆ" redis 5 min ago Up 5 min 6379/tcp
nc-php nextcloud:fpm "docker-entrypoint.sโฆ" php 5 min ago Up 5 min 9000/tcp
nc-nginx nginx:latest "/docker-entrypoint.โฆ" nginx 5 min ago Up 5 min 0.0.0.0:8080->80/tcp
$ docker compose ps
NAME IMAGE COMMAND SERVICE CREATED STATUS PORTS
nc-db mariadb:11 "docker-entrypoint.sโฆ" db 5 min ago Up 5 min 3306/tcp
nc-redis redis:8.6 "docker-entrypoint.sโฆ" redis 5 min ago Up 5 min 6379/tcp
nc-php nextcloud:fpm "docker-entrypoint.sโฆ" php 5 min ago Up 5 min 9000/tcp
nc-nginx nginx:latest "/docker-entrypoint.โฆ" nginx 5 min ago Up 5 min 0.0.0.0:8080->80/tcp
$ docker compose down --volumes
$ docker compose down --volumes
$ docker compose down --volumes
$ rm -rf html/
$ rm -rf html/
$ rm -rf html/
from http.server import HTTPServer, BaseHTTPRequestHandler
import urllib.parse, redis r = redis.Redis(host='redis', port=6379, decode_responses=True) class Handler(BaseHTTPRequestHandler): def do_GET(self): count = r.llen('jobs') self.send_response(200) self.send_header("Content-Type", "text/html") self.end_headers() self.wfile.write(f"<h2>Job Queue</h2><p>{count} jobs in queue</p><form method='post'><input name='job' placeholder='Enter job name'><button>Submit</button></form>".encode()) def do_POST(self): length = int(self.headers.get("Content-Length", 0)) job = urllib.parse.parse_qs(self.rfile.read(length).decode())["job"][0] r.lpush('jobs', job) self.send_response(302) self.send_header("Location", "/") self.end_headers() def log_message(self, format, *args): pass HTTPServer(("0.0.0.0", 5000), Handler).serve_forever()
from http.server import HTTPServer, BaseHTTPRequestHandler
import urllib.parse, redis r = redis.Redis(host='redis', port=6379, decode_responses=True) class Handler(BaseHTTPRequestHandler): def do_GET(self): count = r.llen('jobs') self.send_response(200) self.send_header("Content-Type", "text/html") self.end_headers() self.wfile.write(f"<h2>Job Queue</h2><p>{count} jobs in queue</p><form method='post'><input name='job' placeholder='Enter job name'><button>Submit</button></form>".encode()) def do_POST(self): length = int(self.headers.get("Content-Length", 0)) job = urllib.parse.parse_qs(self.rfile.read(length).decode())["job"][0] r.lpush('jobs', job) self.send_response(302) self.send_header("Location", "/") self.end_headers() def log_message(self, format, *args): pass HTTPServer(("0.0.0.0", 5000), Handler).serve_forever()
from http.server import HTTPServer, BaseHTTPRequestHandler
import urllib.parse, redis r = redis.Redis(host='redis', port=6379, decode_responses=True) class Handler(BaseHTTPRequestHandler): def do_GET(self): count = r.llen('jobs') self.send_response(200) self.send_header("Content-Type", "text/html") self.end_headers() self.wfile.write(f"<h2>Job Queue</h2><p>{count} jobs in queue</p><form method='post'><input name='job' placeholder='Enter job name'><button>Submit</button></form>".encode()) def do_POST(self): length = int(self.headers.get("Content-Length", 0)) job = urllib.parse.parse_qs(self.rfile.read(length).decode())["job"][0] r.lpush('jobs', job) self.send_response(302) self.send_header("Location", "/") self.end_headers() def log_message(self, format, *args): pass HTTPServer(("0.0.0.0", 5000), Handler).serve_forever()
import redis, time, random r = redis.Redis(host='redis', port=6379, decode_responses=True) print("Worker ready. Waiting for jobs...")
while True: job = r.brpop('jobs', timeout=0) if job: _, task = job print(f"Processing {task}...") time.sleep(random.randint(30, 60)) print(f"Completed {task}")
import redis, time, random r = redis.Redis(host='redis', port=6379, decode_responses=True) print("Worker ready. Waiting for jobs...")
while True: job = r.brpop('jobs', timeout=0) if job: _, task = job print(f"Processing {task}...") time.sleep(random.randint(30, 60)) print(f"Completed {task}")
import redis, time, random r = redis.Redis(host='redis', port=6379, decode_responses=True) print("Worker ready. Waiting for jobs...")
while True: job = r.brpop('jobs', timeout=0) if job: _, task = job print(f"Processing {task}...") time.sleep(random.randint(30, 60)) print(f"Completed {task}")
$ mkdir -p prodwork && cd prodwork
$ mkdir -p prodwork && cd prodwork
$ mkdir -p prodwork && cd prodwork
services: redis: image: redis:latest producer: image: python:slim command: sh -c "pip install redis && python -u /app/producer.py" ports: - "5000:5000" volumes: - ./producer.py:/app/producer.py working_dir: /app worker: image: python:slim command: sh -c "pip install redis && python -u /app/worker.py" volumes: - ./worker.py:/app/worker.py working_dir: /app
services: redis: image: redis:latest producer: image: python:slim command: sh -c "pip install redis && python -u /app/producer.py" ports: - "5000:5000" volumes: - ./producer.py:/app/producer.py working_dir: /app worker: image: python:slim command: sh -c "pip install redis && python -u /app/worker.py" volumes: - ./worker.py:/app/worker.py working_dir: /app
services: redis: image: redis:latest producer: image: python:slim command: sh -c "pip install redis && python -u /app/producer.py" ports: - "5000:5000" volumes: - ./producer.py:/app/producer.py working_dir: /app worker: image: python:slim command: sh -c "pip install redis && python -u /app/worker.py" volumes: - ./worker.py:/app/worker.py working_dir: /app
$ docker network ls | grep prodwork
80e4fc2182b5 prodwork_default bridge local
$ docker network ls | grep prodwork
80e4fc2182b5 prodwork_default bridge local
$ docker network ls | grep prodwork
80e4fc2182b5 prodwork_default bridge local
$ docker compose up -d
$ docker compose up -d
$ docker compose up -d
$ docker compose logs -f worker
worker | Worker ready. Waiting for jobs...
$ docker compose logs -f worker
worker | Worker ready. Waiting for jobs...
$ docker compose logs -f worker
worker | Worker ready. Waiting for jobs...
worker | Processing "Generate monthly report"...
worker | Completed Generate monthly report
worker | Processing "Generate monthly report"...
worker | Completed Generate monthly report
worker | Processing "Generate monthly report"...
worker | Completed Generate monthly report
from http.server import HTTPServer, BaseHTTPRequestHandler
import socket, random, time colors = [ "#e74c3c", "#c0392b", "#8e44ad", "#2c3e50", "#2980b9", "#16a085", "#27ae60", "#d35400", "#f39c12", "#2d3436",
]
color = random.choice(colors)
requests = 0
started = time.time() class Handler(BaseHTTPRequestHandler): def do_GET(self): global requests if self.path == "/favicon.ico": self.send_response(204) self.end_headers() return requests += 1 self.send_response(200) self.send_header("Content-Type", "text/html") self.end_headers() hostname = socket.gethostname() ip = socket.gethostbyname(hostname) uptime = int(time.time() - started) html = f"""<html><body style="background:{color};font-family:monospace;text-align:center;padding-top:10%"> <h1 style="font-size:4em;color:white">{hostname}</h1> <p style="font-size:1.8em;color:white">{ip}</p> <p style="font-size:1.4em;color:white">Requests: {requests} | Uptime: {uptime}s</p> </body></html>""" self.wfile.write(html.encode()) def log_message(self, format, *args): pass HTTPServer(("0.0.0.0", 5000), Handler).serve_forever()
from http.server import HTTPServer, BaseHTTPRequestHandler
import socket, random, time colors = [ "#e74c3c", "#c0392b", "#8e44ad", "#2c3e50", "#2980b9", "#16a085", "#27ae60", "#d35400", "#f39c12", "#2d3436",
]
color = random.choice(colors)
requests = 0
started = time.time() class Handler(BaseHTTPRequestHandler): def do_GET(self): global requests if self.path == "/favicon.ico": self.send_response(204) self.end_headers() return requests += 1 self.send_response(200) self.send_header("Content-Type", "text/html") self.end_headers() hostname = socket.gethostname() ip = socket.gethostbyname(hostname) uptime = int(time.time() - started) html = f"""<html><body style="background:{color};font-family:monospace;text-align:center;padding-top:10%"> <h1 style="font-size:4em;color:white">{hostname}</h1> <p style="font-size:1.8em;color:white">{ip}</p> <p style="font-size:1.4em;color:white">Requests: {requests} | Uptime: {uptime}s</p> </body></html>""" self.wfile.write(html.encode()) def log_message(self, format, *args): pass HTTPServer(("0.0.0.0", 5000), Handler).serve_forever()
from http.server import HTTPServer, BaseHTTPRequestHandler
import socket, random, time colors = [ "#e74c3c", "#c0392b", "#8e44ad", "#2c3e50", "#2980b9", "#16a085", "#27ae60", "#d35400", "#f39c12", "#2d3436",
]
color = random.choice(colors)
requests = 0
started = time.time() class Handler(BaseHTTPRequestHandler): def do_GET(self): global requests if self.path == "/favicon.ico": self.send_response(204) self.end_headers() return requests += 1 self.send_response(200) self.send_header("Content-Type", "text/html") self.end_headers() hostname = socket.gethostname() ip = socket.gethostbyname(hostname) uptime = int(time.time() - started) html = f"""<html><body style="background:{color};font-family:monospace;text-align:center;padding-top:10%"> <h1 style="font-size:4em;color:white">{hostname}</h1> <p style="font-size:1.8em;color:white">{ip}</p> <p style="font-size:1.4em;color:white">Requests: {requests} | Uptime: {uptime}s</p> </body></html>""" self.wfile.write(html.encode()) def log_message(self, format, *args): pass HTTPServer(("0.0.0.0", 5000), Handler).serve_forever()
upstream backend { server web:5000;
} server { listen 80; location / { proxy_pass http://backend; }
}
upstream backend { server web:5000;
} server { listen 80; location / { proxy_pass http://backend; }
}
upstream backend { server web:5000;
} server { listen 80; location / { proxy_pass http://backend; }
}
$ mkdir -p loadbalance && cd loadbalance
$ mkdir -p loadbalance && cd loadbalance
$ mkdir -p loadbalance && cd loadbalance
services: nginx: image: nginx:latest ports: - "8080:80" volumes: - ./nginx.conf:/etc/nginx/conf.d/default.conf web: image: python:slim command: python /app/app.py volumes: - ./app.py:/app/app.py working_dir: /app
services: nginx: image: nginx:latest ports: - "8080:80" volumes: - ./nginx.conf:/etc/nginx/conf.d/default.conf web: image: python:slim command: python /app/app.py volumes: - ./app.py:/app/app.py working_dir: /app
services: nginx: image: nginx:latest ports: - "8080:80" volumes: - ./nginx.conf:/etc/nginx/conf.d/default.conf web: image: python:slim command: python /app/app.py volumes: - ./app.py:/app/app.py working_dir: /app
$ docker compose up -d
$ docker compose up -d
$ docker compose up -d - CloudBeaver + PostgreSQL connected in one compose file
- A four-service Nextcloud stack on a shared network - Ep 1-7 completed. You know Compose basics like single service per file, .env files, and the up/ps/logs/down workflow. - No ports on PostgreSQL. CloudBeaver reaches it on the internal network, so there's no need to expose port 5432 to the host. Only CloudBeaver needs a port mapping since it's the one you access from your browser.
- Each service lists networks: - dtstack. This explicitly connects them to the shared bridge network. Compose would create a default network and connect them automatically, but declaring it explicitly makes the intent clear. - Host: postgres (the service name, not an IP address)
- Database: testdb (from your .env)
- Username: postgres
- Password: docker (from your .env) - Volumes are shared. dbdata and redisdata are defined once at the bottom and used by the services that need them.
- PHP-FPM and nginx both mount ./html. Both services mount the same host directory at the same container path: /var/www/html. When Nextcloud writes an uploaded file to /var/www/html/data/user1/photo.jpg inside the PHP-FPM container, nginx can immediately serve it from the same path. No copying, no syncing, just one shared directory.
- Nginx needs a config to talk to PHP-FPM. The nginx.conf file tells nginx: when you see a .php request, don't serve the raw file. Forward it to the php service on port 9000 via FastCGI. Without this, your browser would download index.php instead of running it. - Database type: MariaDB
- Database user: nextcloud
- Database password: nextcloud
- Database name: nextcloud
- Database host: nc-db - Three services: redis, producer, worker
- All three need to be on the same network
- Use python:slim for both producer and worker
- Mount producer.py and worker.py into their respective containers
- Producer needs port 5000 exposed
- Worker uses brpop which blocks until a job is available - Two services: nginx and web (python:slim)
- Nginx needs port 8080 mapped to 80
- Nginx mounts nginx.conf
- Web mounts app.py, runs on port 5000 internally (no host port needed)
- Both on the same network (or just let Compose create the default) - LinkedIn: Share with your network
- Twitter: Tweet about it
- Questions? Drop a comment below or reach out on LinkedIn