├── Dockerfile ├── README.md ├── docker-compose.yml ├── supervisord.conf └── traefik.yml /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM nextcloud 2 | 3 | RUN apt update && apt install -y \ 4 | supervisor nano \ 5 | && rm -rf /var/lib/apt/lists/* \ 6 | && mkdir /var/log/supervisord /var/run/supervisord 7 | 8 | COPY supervisord.conf /etc/supervisor/supervisord.conf 9 | 10 | 11 | ENV NEXTCLOUD_UPDATE=1 12 | 13 | CMD ["/usr/bin/supervisord"] -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ### Nextcloud (with cron inside) + Traefik 2 (LetsEncrypt ACMEv2) + Jellyfin + OnlyOffice + Redis + BitwardenRS (Docker Compose Setup guide) 2 | 3 | READ EVERYTHING BEFORE INSTALL 4 | 5 | ### 1. OS requirements 6 | 7 | This setup requires the latest Docker and Docker-compose versions (I am running on 19.03.5) 8 | 9 | 10 | 11 | ### 2. Domain provider requirements 12 | 13 | Any provider that is supported by ACME can be used: 14 | 15 | 16 | 17 | This setup also uses wildcard certificate, so only one certificate is used for all your domain. Your provider must support DNS-01 challenge to use wildcard certificates. 18 | 19 | You will need to get the API access keys before the install, and set the environment variables accordingly. 20 | 21 | ### 3. Setup 22 | 23 | Just complete the docker-compose.yml and traefik.yml with the data (Domains, passwords and provider API keys) 24 | 25 | Then bring everything up with: 26 | 27 | ``` 28 | sudo docker-compose up -d 29 | ``` 30 | 31 | And give it some time until everything starts, and the certificate is requested 32 | 33 | ### 4. After setup 34 | 35 | Go to Nextcloud, create your user, enter to the main files pages, and then go to the server shell and get inside the container using: 36 | 37 | ``` 38 | sudo docker exec -it -u www-data nextcloud bash 39 | nano config/config.php 40 | ``` 41 | 42 | Add the following (inside the config PHP array): 43 | 44 | ``` 45 | 'overwriteprotocol' => 'https', 46 | ``` 47 | 48 | So the resources can be loaded correctly. 49 | 50 | Also, check that the background jobs, in nextcloud configuration (webpage, not files) is checked to cron 51 | 52 | The files acme.json and access.log that are created on the folder are the LetsEncrypt cert, and the Traefik access logs. 53 | 54 | ### 5. What is installed 55 | 56 | * Nextcloud with cron inside (Thats because is doing a custom build of the nextcloud image) 57 | * Traefik 2 using a secure dashboard, accesible via user and password defined in the basic-auth middleware, with the subdomain traefik 58 | * Redis for Nextcloud 59 | * Jellyfin, with the volume of Nextcloud (Read only) 60 | * OnlyOffice DocumentServer protected with jwt-secret password (environment variable) 61 | * BitwardenRS 62 | 63 | ### 6. Security features 64 | 65 | * Gets A+ on Nextcloud security test 66 | * Gets A+ on ssllabs.com 67 | * HTTPS redirect 68 | * Security headers 69 | * Secure traefik dashboard access 70 | * Perfect Forward Secrecy 71 | * STS preload 72 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3.7" 2 | 3 | services: 4 | traefik: 5 | image: traefik:v2.0.4 6 | restart: unless-stopped 7 | container_name: traefik 8 | hostname: traefik 9 | networks: 10 | - proxy 11 | ports: 12 | - 80:80 13 | - 443:443 14 | volumes: 15 | - ./:/etc/traefik/ 16 | environment: 17 | OVH_ENDPOINT: 18 | OVH_APPLICATION_KEY: 19 | OVH_APPLICATION_SECRET: 20 | OVH_CONSUMER_KEY: 21 | 22 | nextcloud: 23 | build: . 24 | restart: unless-stopped 25 | container_name: nextcloud 26 | hostname: nextcloud 27 | networks: 28 | - proxy 29 | depends_on: 30 | - db 31 | - redis 32 | - documentserver 33 | volumes: 34 | - nextcloud-main:/var/www/html 35 | - nextcloud-apps:/var/www/html/custom_apps 36 | - nextcloud-config:/var/www/html/config 37 | - nextcloud-data:/var/www/html/data 38 | environment: 39 | NEXTCLOUD_TRUSTED_DOMAINS: "cloud.domain.tld" 40 | REDIS_HOST: redis 41 | REDIS_HOST_PASSWORD: password 42 | POSTGRES_DB: nextcloud 43 | POSTGRES_USER: superUser 44 | POSTGRES_PASSWORD: password 45 | POSTGRES_HOST: "db:5432" 46 | 47 | documentserver: 48 | image: onlyoffice/documentserver 49 | restart: unless-stopped 50 | container_name: documentserver 51 | hostname: documentserver 52 | networks: 53 | - proxy 54 | depends_on: 55 | - db 56 | volumes: 57 | - ds-logs:/var/log/onlyoffice 58 | - ds-cache:/var/lib/onlyoffice 59 | environment: 60 | JWT_ENABLED: "true" 61 | JWT_SECRET: password 62 | 63 | db: 64 | image: postgres:12.1 65 | restart: unless-stopped 66 | container_name: db 67 | hostname: db 68 | networks: 69 | - proxy 70 | volumes: 71 | - db-data:/var/lib/postgresql/data 72 | environment: 73 | POSTGRES_USER: superUser 74 | POSTGRES_PASSWORD: password 75 | 76 | redis: 77 | image: redis:5.0 78 | restart: unless-stopped 79 | container_name: redis 80 | hostname: redis 81 | environment: 82 | REDIS_PASSWORD: password 83 | volumes: 84 | - redis-db:/bitnami/redis/data 85 | - redis-config:/opt/bitnami/redis/etc/redis.conf 86 | networks: 87 | - proxy 88 | 89 | jellyfin: 90 | image: jellyfin/jellyfin:10.4.1 91 | restart: unless-stopped 92 | container_name: jellyfin 93 | hostname: jellyfin 94 | volumes: 95 | - jellyfin-config:/config 96 | - jellyfin-cache:/cache 97 | - nextcloud-data:/media:ro 98 | networks: 99 | - proxy 100 | 101 | bitwarden: 102 | image: bitwardenrs/server:1.12.0 103 | restart: unless-stopped 104 | container_name: bitwarden 105 | hostname: bitwarden 106 | networks: 107 | - proxy 108 | volumes: 109 | - bw-data:/data 110 | environment: 111 | WEBSOCKET_ENABLED: "true" 112 | SIGNUPS_ALLOWED: "true" 113 | 114 | volumes: 115 | nextcloud-main: 116 | nextcloud-apps: 117 | nextcloud-config: 118 | nextcloud-data: 119 | ds-logs: 120 | ds-cache: 121 | db-data: 122 | redis-db: 123 | redis-config: 124 | jellyfin-config: 125 | jellyfin-cache: 126 | bw-data: 127 | 128 | networks: 129 | proxy: 130 | name: proxy 131 | -------------------------------------------------------------------------------- /supervisord.conf: -------------------------------------------------------------------------------- 1 | [supervisord] 2 | nodaemon=true 3 | logfile=/var/log/supervisord/supervisord.log 4 | pidfile=/var/run/supervisord/supervisord.pid 5 | childlogdir=/var/log/supervisord/ 6 | logfile_maxbytes=50MB ; maximum size of logfile before rotation 7 | logfile_backups=10 ; number of backed up logfiles 8 | loglevel=error 9 | 10 | [program:apache2] 11 | stdout_logfile=/dev/stdout 12 | stdout_logfile_maxbytes=0 13 | stderr_logfile=/dev/stderr 14 | stderr_logfile_maxbytes=0 15 | command=apache2-foreground 16 | 17 | [program:cron] 18 | stdout_logfile=/dev/stdout 19 | stdout_logfile_maxbytes=0 20 | stderr_logfile=/dev/stderr 21 | stderr_logfile_maxbytes=0 22 | command=/cron.sh -------------------------------------------------------------------------------- /traefik.yml: -------------------------------------------------------------------------------- 1 | ## Static configuration 2 | log: 3 | level: INFO 4 | 5 | accessLog: 6 | filePath: /etc/traefik/access.log 7 | bufferingSize: 1000 8 | 9 | api: 10 | dashboard: true 11 | 12 | providers: 13 | file: 14 | filename: /etc/traefik/traefik.yml 15 | watch: true 16 | 17 | entryPoints: 18 | web: 19 | address: ":80" 20 | 21 | web-secure: 22 | address: ":443" 23 | 24 | certificatesResolvers: 25 | letsencrypt: 26 | acme: 27 | caServer: https://acme-v02.api.letsencrypt.org/directory 28 | email: YOUR EMAIL 29 | storage: /etc/traefik/acme.json 30 | dnsChallenge: 31 | provider: your-domain-provider 32 | delayBeforeCheck: 0 33 | 34 | tls: 35 | options: 36 | default: 37 | sniStrict: true 38 | minVersion: VersionTLS12 39 | cipherSuites: 40 | - TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 41 | - TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 42 | - TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 43 | - TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 44 | 45 | ## Dynamic configuration 46 | http: 47 | routers: 48 | 49 | #DASHBOARD 50 | api-redirect: 51 | rule: Host(`subdomain.doamin.tld`) 52 | entryPoints: 53 | - web 54 | middlewares: 55 | - https-redirect 56 | service: api@internal 57 | 58 | api-router: 59 | rule: Host(`subdomain.doamin.tld`) 60 | entryPoints: 61 | - web-secure 62 | middlewares: 63 | - security-headers 64 | - api-auth 65 | service: api@internal 66 | tls: 67 | certResolver: letsencrypt 68 | domains: 69 | - main: "*.doamin.tld" 70 | options: default 71 | 72 | #NEXTCLOUD 73 | nextcloud-redirect: 74 | rule: Host(`subdomain.doamin.tld`) 75 | entryPoints: 76 | - web 77 | middlewares: 78 | - https-redirect 79 | service: nextcloud 80 | 81 | nextcloud-router: 82 | rule: Host(`subdomain.doamin.tld`) 83 | entryPoints: 84 | - web-secure 85 | middlewares: 86 | - security-headers 87 | - nextcloud-redirect 88 | service: nextcloud 89 | tls: 90 | certResolver: letsencrypt 91 | domains: 92 | - main: "*.doamin.tld" 93 | options: default 94 | 95 | #BITWARDEN UI 96 | bitwarden-ui-redirect: 97 | rule: Host(`subdomain.doamin.tld`) 98 | entryPoints: 99 | - web 100 | middlewares: 101 | - https-redirect 102 | service: bitwarden-ui 103 | 104 | bitwarden-ui-router: 105 | rule: Host(`subdomain.doamin.tld`) 106 | entryPoints: 107 | - web-secure 108 | middlewares: 109 | - security-headers 110 | service: bitwarden-ui 111 | tls: 112 | certResolver: letsencrypt 113 | domains: 114 | - main: "*.doamin.tld" 115 | options: default 116 | 117 | #BITWARDEN WEBSOCKET 118 | bitwarden-websocket-router: 119 | rule: Host(`subdomain.doamin.tld`) && Path(`/notifications/hub`) 120 | entryPoints: 121 | - web-secure 122 | middlewares: 123 | - security-headers 124 | service: bitwarden-websocket 125 | tls: 126 | certResolver: letsencrypt 127 | domains: 128 | - main: "*.doamin.tld" 129 | options: default 130 | 131 | #JELLYFIN 132 | jellyfin-redirect: 133 | rule: Host(`subdomain.doamin.tld`) 134 | entryPoints: 135 | - web 136 | middlewares: 137 | - https-redirect 138 | service: jellyfin 139 | 140 | jellyfin-router: 141 | rule: Host(`subdomain.doamin.tld`) 142 | entryPoints: 143 | - web-secure 144 | middlewares: 145 | - security-headers 146 | service: jellyfin 147 | tls: 148 | certResolver: letsencrypt 149 | domains: 150 | - main: "*.doamin.tld" 151 | options: default 152 | 153 | #DOCUMENTSERVER 154 | documentserver-redirect: 155 | rule: Host(`subdomain.doamin.tld`) 156 | entryPoints: 157 | - web 158 | middlewares: 159 | - https-redirect 160 | service: documentserver 161 | 162 | documentserver-router: 163 | rule: Host(`subdomain.doamin.tld`) 164 | entryPoints: 165 | - web-secure 166 | middlewares: 167 | - security-headers 168 | service: documentserver 169 | tls: 170 | certResolver: letsencrypt 171 | domains: 172 | - main: "*.doamin.tld" 173 | options: default 174 | 175 | middlewares: 176 | https-redirect: 177 | redirectScheme: 178 | scheme: https 179 | 180 | api-auth: 181 | basicAuth: 182 | users: 183 | - dashbOardUser:password-hash 184 | 185 | security-headers: 186 | headers: 187 | referrerPolicy: no-referrer 188 | forceSTSHeader: true 189 | stsSeconds: 31536000 190 | stsIncludeSubdomains: true 191 | stsPreload: true 192 | contentTypeNosniff: true 193 | browserXssFilter: true 194 | customRequestHeaders: 195 | X-Forwarded-Proto: https 196 | customResponseHeaders: 197 | X-Powered-By: "Redstone" 198 | Server: "Server" 199 | 200 | nextcloud-redirect: 201 | redirectRegex: 202 | permanent: true 203 | regex: http://(.*)/.well-known/(card|cal)dav 204 | replacement: http://$$1/remote.php/dav/ 205 | 206 | services: 207 | nextcloud: 208 | loadBalancer: 209 | servers: 210 | - url: http://nextcloud:80 211 | 212 | jellyfin: 213 | loadBalancer: 214 | servers: 215 | - url: http://jellyfin:8096 216 | 217 | bitwarden-ui: 218 | loadBalancer: 219 | servers: 220 | - url: http://bitwarden:80 221 | 222 | bitwarden-websocket: 223 | loadBalancer: 224 | servers: 225 | - url: ws://bitwarden:3012 226 | 227 | documentserver: 228 | loadBalancer: 229 | servers: 230 | - url: http://documentserver:80 231 | --------------------------------------------------------------------------------