├── 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 |
--------------------------------------------------------------------------------