upstream megarepo {
server 127.0.0.1:8080;
keepalive 32;
}
server {
listen 80;
server_name megarepo.example.com;
location / {
return 301 https://$host$request_uri;
}
location /.well-known/acme-challenge/ {
root /var/www/certbot;
}
}
server {
listen 443 ssl;
http2 on;
server_name megarepo.example.com;
ssl_certificate /etc/letsencrypt/live/megarepo.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/megarepo.example.com/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers off;
ssl_session_cache shared:SSL:10m;
ssl_session_tickets off;
add_header Strict-Transport-Security "max-age=63072000" always;
client_max_body_size 0;
chunked_transfer_encoding on;
location / {
proxy_pass http://megarepo;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_read_timeout 900;
proxy_send_timeout 900;
proxy_request_buffering off;
proxy_buffering off;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
}
}
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
services:
megarepo:
image: bsnsoft/megarepo:latest
restart: unless-stopped
environment:
SPRING_DATASOURCE_URL: jdbc:postgresql://db:5432/megarepo?stringtype=unspecified
SPRING_DATASOURCE_USERNAME: megarepo
SPRING_DATASOURCE_PASSWORD: ${DB_PASSWORD}
MEGAREPO_JWT_SECRET: ${JWT_SECRET}
SERVER_FORWARD_HEADERS_STRATEGY: native
volumes: ["megarepo-data:/app/data"]
depends_on:
db: { condition: service_healthy }
networks: [internal]
db:
image: postgres:16-alpine
restart: unless-stopped
environment:
POSTGRES_DB: megarepo
POSTGRES_USER: megarepo
POSTGRES_PASSWORD: ${DB_PASSWORD}
volumes: ["postgres-data:/var/lib/postgresql/data"]
healthcheck:
test: ["CMD-SHELL", "pg_isready -U megarepo"]
interval: 5s
retries: 5
networks: [internal]
nginx:
image: nginx:1.27-alpine
restart: unless-stopped
ports: ["80:80", "443:443"]
volumes:
- ./nginx.conf:/etc/nginx/conf.d/default.conf:ro
- certbot-etc:/etc/letsencrypt:ro
- certbot-var:/var/www/certbot:ro
depends_on: [megarepo]
networks: [internal]
certbot:
image: certbot/certbot
volumes:
- certbot-etc:/etc/letsencrypt
- certbot-var:/var/www/certbot
entrypoint: "/bin/sh -c 'trap exit TERM; while :; do certbot renew --quiet; sleep 12h & wait $${!}; done'"
volumes:
megarepo-data:
postgres-data:
certbot-etc:
certbot-var:
networks:
internal:
cat > .env <<EOF
DB_PASSWORD=$(openssl rand -base64 32)
JWT_SECRET=$(openssl rand -base64 64)
EOF
docker compose -f docker-compose.prod.yml up -d nginx
docker compose -f docker-compose.prod.yml run --rm certbot \
certonly --webroot --webroot-path=/var/www/certbot \
--email admin@example.com --agree-tos --no-eff-email \
-d megarepo.example.com
docker compose -f docker-compose.prod.yml down
docker compose -f docker-compose.prod.yml up -d
echo "0 5 * * * cd /opt/megarepo && docker compose -f docker-compose.prod.yml run --rm certbot renew --quiet && docker compose -f docker-compose.prod.yml exec nginx nginx -s reload" \
| sudo tee /etc/cron.d/megarepo-certbot
curl https://megarepo.example.com/api/v1/status
docker login megarepo.example.com
docker pull megarepo.example.com/docker-hosted/alpine:latest