services: hub: image: git.necloud.us/nate/hub:latest # built by the Gitea workflow; `docker compose build` builds locally instead build: . restart: unless-stopped # The container looks names up through these. If necloud.us only exists on your home/LAN DNS # (Pi-hole, router, AdGuard...), set DNS_1 to that server's IP instead. dns: - ${DNS_1:-1.1.1.1} - ${DNS_2:-8.8.8.8} ports: - "8080:8080" environment: # Edit the values here, or leave the ${...} placeholders and set them in a .env file / your stack's env UI. SEAFILE_URL: ${SEAFILE_URL:-https://seafile.necloud.us} ONLYOFFICE_URL: ${ONLYOFFICE_URL:-https://onlyoffice.necloud.us} AUDIOBOOKS_URL: ${AUDIOBOOKS_URL:-https://audiobooks.necloud.us} AUDIOBOOKS_LOGIN_LINK: ${AUDIOBOOKS_LOGIN_LINK:-} AUDIOBOOKS_SSO_SECRET: ${AUDIOBOOKS_SSO_SECRET:-} AUDIOBOOKS_SSO_URL: ${AUDIOBOOKS_SSO_URL:-} VERSE_URL: ${VERSE_URL:-https://versebyversewithnate.us/admin} WEBMAIL_URL: ${WEBMAIL_URL:-} # optional: a webmail elsewhere; leave empty to use the bundled Roundcube below ROUNDCUBE_URL: http://roundcube:80 # the bundled Roundcube; the hub shows it at /webmail/ (it is not exposed to the internet) PODCAST_URL: ${PODCAST_URL:-https://podcast.necloud.us/} ONLYOFFICE_JWT_SECRET: ${ONLYOFFICE_JWT_SECRET:-} # same value as the OnlyOffice server's JWT secret PUBLIC_URL: ${PUBLIC_URL:-} # e.g. https://hub.necloud.us (OnlyOffice posts saved edits here) HUB_SETUP_CODE: ${HUB_SETUP_CODE:-} # first passkey; if empty, a code is printed in the log SESSION_DAYS: ${SESSION_DAYS:-180} # how long you stay signed in when idle; using the app renews it COOKIE_SECRET: ${COOKIE_SECRET:-} # optional; auto-generated and kept in /data if empty depends_on: - roundcube volumes: - hub-data:/data # passkeys, calendar links and the session secret live here # Webmail shown inside the Mail tab. Only the hub can reach it (no published port), so it sits behind the passkey sign-in. # Sign in to it with your mail address and an app password (Gmail, iCloud and Yahoo are preset in the config below). roundcube: image: roundcube/roundcubemail:latest-apache restart: unless-stopped environment: ROUNDCUBEMAIL_DB_TYPE: sqlite ROUNDCUBEMAIL_REQUEST_PATH: /webmail/ ROUNDCUBEMAIL_PLUGINS: archive,zipdownload ROUNDCUBEMAIL_UPLOAD_MAX_FILESIZE: 30M # Microsoft 365 sign-in (optional, see README -> Mail). Leave empty to skip. M365_CLIENT_ID: ${M365_CLIENT_ID:-} M365_CLIENT_SECRET: ${M365_CLIENT_SECRET:-} M365_TENANT: ${M365_TENANT:-} PUBLIC_URL: ${PUBLIC_URL:-} configs: - source: roundcube_hub target: /var/roundcube/config/hub.php volumes: - roundcube-data:/var/roundcube/db # address book, settings and the encryption key configs: roundcube_hub: content: | 'Gmail', 'ssl://imap.mail.me.com:993' => 'iCloud Mail', 'ssl://imap.mail.yahoo.com:993' => 'Yahoo Mail', ]; // Microsoft 365 through OAuth 2.0 (needs an app registration, see README). It goes first because that is where OAuth sign-ins are sent. if (getenv('M365_CLIENT_ID')) { $$hosts = ['ssl://outlook.office365.com:993' => 'Microsoft 365 (use the Microsoft button)'] + $$hosts; $$tenant = getenv('M365_TENANT') ?: 'organizations'; $$config['oauth_provider'] = 'generic'; $$config['oauth_provider_name'] = 'Microsoft 365'; $$config['oauth_client_id'] = getenv('M365_CLIENT_ID'); $$config['oauth_client_secret'] = getenv('M365_CLIENT_SECRET'); $$config['oauth_auth_uri'] = "https://login.microsoftonline.com/$${tenant}/oauth2/v2.0/authorize"; $$config['oauth_token_uri'] = "https://login.microsoftonline.com/$${tenant}/oauth2/v2.0/token"; $$config['oauth_identity_uri'] = 'https://graph.microsoft.com/oidc/userinfo'; $$config['oauth_identity_fields'] = ['email', 'preferred_username']; $$config['oauth_scope'] = 'email openid profile offline_access https://outlook.office365.com/IMAP.AccessAsUser.All https://outlook.office365.com/SMTP.Send'; $$config['oauth_login_redirect'] = false; if (getenv('PUBLIC_URL')) $$config['oauth_redirect_uri'] = rtrim(getenv('PUBLIC_URL'), '/') . '/webmail/index.php/login/oauth'; } $$config['imap_host'] = $$hosts; $$config['smtp_host'] = 'tls://smtp.%z:587'; // smtp.gmail.com, smtp.mail.me.com, smtp.mail.yahoo.com $$config['session_lifetime'] = 43200; // minutes (30 days); the hub's own sign-in is what protects access // Behind the hub: believe its forwarded address and https. if (($$_SERVER['HTTP_X_FORWARDED_PROTO'] ?? '') === 'https') $$_SERVER['HTTPS'] = 'on'; if (!empty($$_SERVER['HTTP_X_FORWARDED_HOST'])) $$_SERVER['HTTP_HOST'] = $$_SERVER['HTTP_X_FORWARDED_HOST']; // A fixed encryption key kept with the data, so saved settings and sessions survive restarts and updates. $$kf = '/var/roundcube/db/des_key'; if (!is_file($$kf)) @file_put_contents($$kf, bin2hex(random_bytes(12))); if (is_file($$kf)) $$config['des_key'] = trim(file_get_contents($$kf)); volumes: hub-data: name: nehub-data # fixed name so the data survives redeploys and renamed stacks roundcube-data: name: nehub-roundcube