Skip to content

Docker tips, example compose files, & troubleshooting

Compose examples

Running compose projects

docker compose up -d
docker compose down
docker compose up -d --force-recreate
docker compose pull

Traefik docker-compose.yml

Notes:

  • 💡CHANGE ME💡
  • Use environment variables: .env file and ${ENV_VAR} in your code. Do not expose secrets in public repositories.* This example uses Cloudflare for SSL/TLS
version: '3'

services:
  traefik:
    image: traefik:latest
    container_name: traefik
    restart: unless-stopped
    security_opt:
      - no-new-privileges:true
    networks:
      - proxy
    ports:
      - 80:80
      - 443:443
    environment:
      - CF_DNS_API_TOKEN=${CF_API_TOKEN}
      # Docs on how to generate an API token in Cloudflare: [https://developers.cloudflare.com/fundamentals/api/get-started/create-token/#:~:text=From%20the%20Cloudflare%20dashboard%20%E2%86%97,Select%20Create%20Token.](https://developers.cloudflare.com/fundamentals/api/get-started/create-token/#:~:text=From%20the%20Cloudflare%20dashboard%20%E2%86%97,Select%20Create%20Token.)
    volumes:
      - /etc/localtime:/etc/localtime:ro
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - /home/${HOME_DIR_USER}/traefik/data/traefik.yml:/traefik.yml:ro
      - /home/${HOME_DIR_USER}/traefik/data/acme.json:/acme.json
      - /home/${HOME_DIR_USER}/traefik/data/config.yml:/config.yml:ro
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.traefik.entrypoints=http"
      - "traefik.http.routers.traefik.rule=Host(`💡traefik.yourdomain.com💡`)"
      - "traefik.http.middlewares.traefik-auth.basicauth.users=${HTTP_BASIC_USER}:${HTTP_BASIC_PWD}"
      - "traefik.http.middlewares.traefik-https-redirect.redirectscheme.scheme=https"
      - "traefik.http.middlewares.sslheader.headers.customrequestheaders.X-Forwarded-Proto=https"
      - "traefik.http.routers.traefik.middlewares=traefik-https-redirect"
      - "traefik.http.routers.traefik-secure.entrypoints=https"
      - "traefik.http.routers.traefik-secure.rule=Host(`💡traefik.yourdomain.com💡`)"
      - "traefik.http.routers.traefik-secure.middlewares=traefik-auth"
      - "traefik.http.routers.traefik-secure.tls=true"
      - "traefik.http.routers.traefik-secure.tls.certresolver=cloudflare"
      - "traefik.http.routers.traefik-secure.tls.domains[0].main=💡yourdomain.com💡"
      - "traefik.http.routers.traefik-secure.tls.domains[0].sans=*.💡yourdomain.com💡"
      - "traefik.http.routers.traefik-secure.service=api@internal"

networks:
  proxy:
    external: true

Pterodactyl + Traefik + Cloudflare on Docker Compose

Notes:

  • Use environment variables: .env file and ${ENV_VAR} in your code. Do not expose secrets in public repositories.
  • Wings Daemon: even though the documentation says port 8443 is needed for Cloudflare proxy, I didn't have issues with port 443.
  • Browser console and docker logs can give clues to issues with connecting to the Wings nodes.
  • Do not be led astray by CORS errors, they are possibly issues with the reverse proxy configuration and not actually CORS-related (in some cases).

Credits:

  • Adapted from Jims's Garage, plus a few changes I needed to make it work for me.
  • Pterodactyl Discord

compose.yml:

services:
  db:
    image: mariadb:latest
    container_name: pterodactyl_mariadb
    restart: unless-stopped
    command: --default-authentication-plugin=mysql_native_password
    volumes:
      - ./panel/db:/var/lib/mysql
    environment:
      MYSQL_DATABASE: panel
      MYSQL_USER: pterodactyl
      MYSQL_PASSWORD: ${DB_PASSWORD}
      MYSQL_ROOT_PASSWORD: ${DB_ROOT_PASSWORD}
    networks:
      - pterodactyl-network

  cache:
    image: redis:alpine
    container_name: pterodactyl_redis
    restart: unless-stopped
    networks:
      - pterodactyl-network

  panel:
    image: ghcr.io/pterodactyl/panel:latest
    container_name: pterodactyl_panel
    restart: unless-stopped
    stdin_open: true
    tty: true
# port required if you do not use a reverse proxy
#    ports:
#      - 8080:80
#      - 8443:443 # OPTIONAL
    volumes:
      - "./panel/var/:/app/var/"
      - "./panel/logs/:/app/storage/logs"
      - "./panel/nginx/:/etc/nginx/conf.d/"
    environment:
      RECAPTCHA_ENABLED: false
      TZ: Etc/UTC
      APP_TIMEZONE: US/Pacific
      APP_KEY: ${APP_KEY}
      APP_ENV: production
      APP_ENVIRONMENT_ONLY: false
      APP_URL: ${PANEL_DOMAIN}
      APP_SERVICE_AUTHOR: ${MAIL_FROM_ADDRESS}
      MAIL_FROM: ${MAIL_FROM_ADDRESS}
      MAIL_DRIVER: smtp
      MAIL_HOST: ${MAIL_HOST}
      MAIL_PORT: ${MAIL_PORT}
      MAIL_USERNAME: ${MAIL_USERNAME}
      MAIL_PASSWORD: ${MAIL_PASSWORD}
      MAIL_ENCRYPTION: false
      TRUSTED_PROXIES: "*"
      PTERODACTYL_TELEMETRY_ENABLED: false
      DB_HOST: db
      DB_PORT: 3306
      DB_USERNAME: pterodactyl
      DB_PASSWORD: ${DB_PASSWORD}
      CACHE_DRIVER: redis
      SESSION_DRIVER: redis
      QUEUE_DRIVER: redis
      REDIS_HOST: cache
#      LE_EMAIL: "" # Uncomment if you want to use Let's Encrypt to generate an SSL certificate for the Panel.
    networks:
      - proxy
      - pterodactyl-network
    labels:
      - "traefik.enable=true"
      - "traefik.docker.network=proxy"
      - "traefik.http.routers.panel.entrypoints=web"
      - "traefik.http.routers.panel.rule=Host(`${PANEL_DOMAIN}`)"
      - "traefik.http.middlewares.panel-https-redirect.redirectscheme.scheme=https"
      - "traefik.http.routers.panel.middlewares=panel-https-redirect"
      - "traefik.http.routers.panel-secure.entrypoints=websecure"
      - "traefik.http.routers.panel-secure.rule=Host(`${PANEL_DOMAIN}`)"
      - "traefik.http.routers.panel-secure.tls=true"
      - "traefik.http.routers.panel-secure.tls.certresolver=cloudflare"
      - "traefik.http.routers.panel-secure.service=panel"
      - "traefik.http.services.panel.loadbalancer.server.port=80" # make sure the loadbalancer is the last line!!!

  wings:
    image: ghcr.io/pterodactyl/wings:latest
    container_name: pterodactyl_wings
    restart: unless-stopped
    ports:
      - 2022:2022 # SFTP
    stdin_open: true
    tty: true
    environment:
      TZ: US/Pacific
      APP_TIMEZONE: US/Pacific
      WINGS_UID: 1000
      WINGS_GID: 1000
      WINGS_USERNAME: pterodactyl
    volumes:
      - "/var/run/docker.sock:/var/run/docker.sock"
      - "/var/lib/docker/containers/:/var/lib/docker/containers/"
      - "/etc/pterodactyl/:/etc/pterodactyl/"
      - "/var/lib/pterodactyl/:/var/lib/pterodactyl/"
      - "/var/log/pterodactyl/:/var/log/pterodactyl/"
      - "/tmp/pterodactyl/:/tmp/pterodactyl/"
      - "/etc/ssl/certs:/etc/ssl/certs:ro"
    networks:
      - proxy
      - wings0
    labels:
      - "traefik.enable=true"
      - "traefik.docker.network=proxy"
      - "traefik.http.routers.wings0.entrypoints=web"
      - "traefik.http.routers.wings0.rule=Host(`${WINGS_DOMAIN}`)"
      - "traefik.http.middlewares.wings0-https-redirect.redirectscheme.scheme=https"
      - "traefik.http.routers.wings0.middlewares=wings0-https-redirect"
      - "traefik.http.routers.wings0-secure.entrypoints=websecure"
      - "traefik.http.routers.wings0-secure.rule=Host(`${WINGS_DOMAIN}`)"
      - "traefik.http.routers.wings0-secure.tls=true"
      - "traefik.http.routers.wings0-secure.tls.certresolver=cloudflare"
      - "traefik.http.routers.wings0-secure.service=wings0"
      - "traefik.http.services.wings0.loadbalancer.server.port=443" # make sure the loadbalancer is the last line!!!

networks:
  pterodactyl-network:
    name: pterodactyl-network
  proxy:
    external: true
  wings0:
    name: wings0
    driver: bridge
    ipam:
      config:
        - subnet: "172.18.0.0/16" # Change this if needed. Make sure this doesn't conflict with existing networks
    driver_opts:
      com.docker.network.bridge.name: wings0

Run:

docker compose up -d

  • Other useful commands if needed:
docker compose up -d --force-recreate

# if you split your compose files between Panel and Wings
docker compose -f compose.panel.yml up -d 
docker compose -f compose.wings.yml up -d 

# Show logs
docker compose logs -f

After the first time Panel is running (Wings will fail at this time):

  • Create admin user, follow terminal input prompts

docker exec -it pterodactyl_panel php artisan p:user:make

  • Create a Location
  • Create a Node
    • Use SSL Connection: Yes
    • Behind Proxy: Yes
    • Daemon Port: 443
  • Save the Node and go to the Configuration tab
  • Copy and paste this to the server local file /etc/pterodactyl/config.yml (create it if it doesn't exist)
  • Copy and paste the following to the end of the above config.yml, otherwise you may get some CORS errors in the browser console.
docker:
  network:
    interface: 172.18.0.1
    dns:
    - 1.1.1.1
    - 1.0.0.1
    name: pterodactyl_nw
    ispn: false
    driver: bridge
    network_mode: pterodactyl_nw
    is_internal: false
    enable_icc: true
    interfaces:
      v4:
        subnet: 172.18.0.0/16
        gateway: 172.18.0.1
      v6:
        subnet: fdba:17c8:6c94::/64
        gateway: fdba:17c8:6c94::1011
  domainname: ""
  registries: {}
  tmpfs_size: 100
- change the `interface`, `subnet`, and `gateway` to match what you put for the `wings0` network in your `compose.yml` file.
  • Check the Nodes List, if you have a green heart icon, you're golden!