# A complete ProjectSend install using the official image. # # 1. Edit the passwords and APP_URL below. # 2. docker compose -f compose.example.yaml up -d # 3. Open APP_URL — the setup screen creates your administrator account. # # This is the file the Docker Hub description points at, so it is written # for someone who has never seen the project before. name: projectsend services: app: image: projectsend/projectsend:2 restart: unless-stopped ports: # Put a TLS-terminating proxy in front of this in any real install. # ProjectSend issues download links and password-reset emails using # APP_URL, so that value — not this port — is what users must reach. # # Bound to the loopback address, not to every interface, because # TRUSTED_PROXIES below is "*". That setting tells the application to # believe the X-Forwarded-For header of whoever connects to it, which # is correct behind a proxy and catastrophic when anybody can connect # directly: a visitor who reaches this port themselves is then the # "proxy", and can hand the application any client IP they like — # which is enough to walk straight through the login lockout, every # named rate limit, and the address recorded in the download log. # # Publishing on the loopback address keeps the proxy (on this host, # or in this compose file) able to reach it while nothing off the # machine can. If you move the proxy to another host, publish on the # interface it comes from and narrow TRUSTED_PROXIES to that address # or subnet at the same time — the two settings only make sense # together. - "127.0.0.1:8080:80" environment: APP_URL: https://files.example.com APP_ENV: production APP_DEBUG: "false" # Generated on first boot and kept on the storage volume. Set it # explicitly if you manage secrets elsewhere — but never change it on # a running install: it decrypts existing data. # APP_KEY: base64:... DB_CONNECTION: mysql DB_HOST: db DB_PORT: "3306" DB_DATABASE: projectsend DB_USERNAME: projectsend DB_PASSWORD: change-me-database REDIS_HOST: redis CACHE_STORE: redis SESSION_DRIVER: redis QUEUE_CONNECTION: redis # Uncomment together with the clamav service at the bottom of this # file. It is written into the settings once, on first boot, so the # site arrives configured — and it stays yours afterwards: the # address and the switch are both on System → Settings → Virus # scanning, and this line is ignored on every later boot. # PROJECTSEND_SCANNER_DEFAULT_ADDRESS: tcp://clamav:3310 # Mail is easier to configure from System → Settings → Email once you # are logged in — it has a "send test" button. These are the fallback # until then. MAIL_MAILER: smtp MAIL_HOST: smtp.example.com MAIL_PORT: "587" MAIL_USERNAME: "" MAIL_PASSWORD: "" MAIL_FROM_ADDRESS: files@example.com # Required whenever anything sits between your visitors and this # container — which includes the reverse proxy you should be running. # Without it every visitor appears to come from the proxy: the login # rate limiter treats all of your users as one attacker, and the # download log records the proxy's address. # # "*" means "trust whoever connects to me", which is only safe when # nothing but the proxy can — which is what the loopback binding # above is for. Change one and you have to change the other. TRUSTED_PROXIES: "*" # Optional: uncomment these — with a password of your own — to create # the first administrator unattended and skip the setup screen. Left # commented, the setup screen creates it instead. Ignored once any # user exists. # ADMIN_NAME: Administrator # ADMIN_EMAIL: admin@example.com # ADMIN_PASSWORD: change-me-admin volumes: # Every uploaded file lives here, along with the generated APP_KEY. # This is the volume to back up; losing it loses the data. - storage:/var/www/html/storage depends_on: db: condition: service_healthy db: image: mysql:8.4 restart: unless-stopped environment: MYSQL_DATABASE: projectsend MYSQL_USER: projectsend MYSQL_PASSWORD: change-me-database MYSQL_ROOT_PASSWORD: change-me-root volumes: - db-data:/var/lib/mysql healthcheck: # The app waits for this before migrating, so a slow first start is # normal rather than a failure. test: ["CMD", "mysqladmin", "ping", "-h", "127.0.0.1", "--silent"] interval: 5s timeout: 5s retries: 20 redis: image: redis:7-alpine restart: unless-stopped volumes: - redis-data:/data # Virus scanning, off unless you ask for it: # docker compose --profile scanner up -d # then point Settings → Virus scanning at tcp://clamav:3310. # # Budget about 1-1.5 GB of memory: the virus definitions are held in # memory. The first start downloads them and does not answer until it # has, which the Test button on that screen reports plainly. clamav: image: clamav/clamav:stable restart: unless-stopped # Deliberately no ports. clamd has no authentication and no # encryption, so anything that can reach it can use it, and files # cross that connection in the clear. volumes: - clamav-data:/var/lib/clamav # ClamAV's own defaults are not enough: left on them, clamd answers # "OK" for an archive it cannot open, and every password-protected zip # would be recorded as clean. The settings it needs are at the bottom # of this file. configs: - source: clamd-conf target: /etc/clamav/clamd.conf profiles: - scanner volumes: storage: db-data: redis-data: clamav-data: configs: clamd-conf: content: | LogTime yes Foreground yes TCPSocket 3310 # The largest stream clamd accepts. Keep it at or above "Largest file # to scan" on the settings screen, or larger files are not scanned. StreamMaxLength 512M MaxFileSize 512M MaxScanSize 1024M MaxRecursion 16 MaxFiles 10000 # Report what could not be opened instead of calling it clean. These # four are the reason this file exists. AlertExceedsMax yes AlertEncrypted yes AlertEncryptedArchive yes AlertEncryptedDoc yes