2 # You can have redis on a different box
3 RAILS_ENV: 'production'
6 # this gives us very good cache coverage, 96 -> 99
7 # in practice it is 1-2% perf improvement
8 RUBY_GLOBAL_METHOD_CACHE_SIZE: 131072
9 # stop heap doubling in size so aggressively, this conserves memory
10 RUBY_GC_HEAP_GROWTH_MAX_SLOTS: 40000
11 RUBY_GC_HEAP_INIT_SLOTS: 400000
12 RUBY_GC_HEAP_OLDOBJECT_LIMIT_FACTOR: 1.5
14 DISCOURSE_DB_SOCKET: /var/run/postgresql
22 home: /var/www/discourse
26 - exec: thpoff echo "thpoff is installed!"
27 - exec: /usr/local/bin/ruby -e 'if ENV["DISCOURSE_SMTP_ADDRESS"] == "smtp.example.com"; puts "Aborting! Mail is not configured!"; exit 1; end'
28 - exec: /usr/local/bin/ruby -e 'if ENV["DISCOURSE_HOSTNAME"] == "discourse.example.com"; puts "Aborting! Domain is not configured!"; exit 1; end'
29 - exec: /usr/local/bin/ruby -e 'if (ENV["DISCOURSE_CDN_URL"] || "")[0..1] == "//"; puts "Aborting! CDN must have a protocol specified. Once fixed you should rebake your posts now to correct all posts."; exit 1; end'
30 - exec: chown -R discourse /home/discourse
31 # TODO: move to base image (anacron can not be fired up using rc.d)
32 - exec: rm -f /etc/cron.d/anacron
34 path: /etc/cron.d/anacron
37 PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
39 30 7 * * * root /usr/sbin/anacron -s >/dev/null
41 path: /etc/runit/1.d/copy-env
46 conf=/var/www/discourse/config/discourse.conf
48 # find DISCOURSE_ env vars, strip the leader, lowercase the key
49 /usr/local/bin/ruby -e 'ENV.each{|k,v| puts "#{$1.downcase} = '\''#{v}'\''" if k =~ /^DISCOURSE_(.*)/}' > $conf
52 path: /etc/service/unicorn/run
60 chown -R discourse:www-data /shared/log/rails
61 LD_PRELOAD=$RUBY_ALLOCATOR HOME=/home/discourse USER=discourse exec thpoff chpst -u discourse:www-data -U discourse:www-data bundle exec config/unicorn_launcher -E production -c config/unicorn.conf.rb
64 path: /etc/service/nginx/run
72 path: /etc/runit/3.d/01-nginx
79 path: /etc/runit/3.d/02-unicorn
91 - git remote set-branches --add origin master
92 - git remote set-branches origin $version
93 - git fetch --depth 1 origin $version
94 - git checkout $version
96 - chown discourse:www-data tmp
98 - mkdir -p tmp/sockets
100 - mkdir -p /shared/log/rails
101 - bash -c "touch -a /shared/log/rails/{production,production_errors,unicorn.stdout,unicorn.stderr,sidekiq}.log"
102 - bash -c "ln -s /shared/log/rails/{production,production_errors,unicorn.stdout,unicorn.stderr,sidekiq}.log $home/log"
103 - bash -c "mkdir -p /shared/{uploads,backups}"
104 - bash -c "ln -s /shared/{uploads,backups} $home/public"
105 - bash -c "mkdir -p /shared/tmp/{backups,restores}"
106 - bash -c "ln -s /shared/tmp/{backups,restores} $home/tmp"
107 - chown -R discourse:www-data /shared/log/rails /shared/uploads /shared/backups /shared/tmp
108 # scrub broken symlinks from plugins that have been removed
109 - find public/plugins/ -maxdepth 1 -xtype l -delete
113 - "cp $home/config/nginx.sample.conf /etc/nginx/conf.d/discourse.conf"
114 - "rm /etc/nginx/sites-enabled/default"
115 - "mkdir -p /var/nginx/cache"
118 filename: /etc/nginx/nginx.conf
119 from: pid /run/nginx.pid;
123 filename: "/etc/nginx/conf.d/discourse.conf"
124 from: /upstream[^\}]+\}/m
125 to: "upstream discourse {
126 server 127.0.0.1:3000;
130 filename: "/etc/nginx/conf.d/discourse.conf"
131 from: /server_name.+$/
135 filename: "/etc/nginx/conf.d/discourse.conf"
136 from: /client_max_body_size.+$/
137 to: client_max_body_size $upload_size ;
140 cmd: echo "done configuring web"
147 # ensure we are on latest bundler
149 - find $home ! -user discourse -exec chown discourse {} \+
155 - su discourse -c 'bundle install --deployment --retry 3 --jobs 4 --verbose --without test development'
160 - su discourse -c 'bundle exec rake plugin:pull_compatible_all'
167 - su discourse -c 'bundle exec rake db:migrate'
170 hook: assets_precompile
172 - su discourse -c 'bundle exec rake themes:update assets:precompile'
175 path: /usr/local/bin/discourse
179 (cd /var/www/discourse && RAILS_ENV=production sudo -H -E -u discourse bundle exec script/discourse "$@")
182 path: /usr/local/bin/rails
186 # If they requested a console, load pry instead
187 if [ "$*" == "c" -o "$*" == "console" ]
189 (cd /var/www/discourse && RAILS_ENV=production sudo -H -E -u discourse bundle exec pry -r ./config/environment)
191 (cd /var/www/discourse && RAILS_ENV=production sudo -H -E -u discourse bundle exec script/rails "$@")
195 path: /usr/local/bin/rake
199 (cd /var/www/discourse && RAILS_ENV=production sudo -H -E -u discourse bundle exec bin/rake "$@")
202 path: /usr/local/bin/rbtrace
206 (cd /var/www/discourse && RAILS_ENV=production sudo -H -E -u discourse bundle exec rbtrace "$@")
209 path: /usr/local/bin/stackprof
213 (cd /var/www/discourse && RAILS_ENV=production sudo -H -E -u discourse bundle exec stackprof "$@")
216 path: /etc/update-motd.d/10-web
221 echo Use: rails, rake or discourse to execute commands in production
225 path: /etc/logrotate.d/rails
227 /shared/log/rails/*.log
241 path: /etc/logrotate.d/nginx
243 /var/log/nginx/*.log {
249 create 0644 www-data www-data
256 # move state out of the container this fancy is done to support rapid rebuilds of containers,
257 # we store anacron and logrotate state outside the container to ensure its maintained across builds
258 # later move this snipped into an intialization script
259 # we also ensure all the symlinks we need to /shared are in place in the correct structure
260 # this allows us to bootstrap on one machine and then run on another
262 path: /etc/runit/1.d/00-ensure-links
266 if [[ ! -L /var/lib/logrotate ]]; then
267 rm -fr /var/lib/logrotate
268 mkdir -p /shared/state/logrotate
269 ln -s /shared/state/logrotate /var/lib/logrotate
271 if [[ ! -L /var/spool/anacron ]]; then
272 rm -fr /var/spool/anacron
273 mkdir -p /shared/state/anacron-spool
274 ln -s /shared/state/anacron-spool /var/spool/anacron
276 if [[ ! -d /shared/log/rails ]]; then
277 mkdir -p /shared/log/rails
278 chown -R discourse:www-data /shared/log/rails
280 if [[ ! -d /shared/uploads ]]; then
281 mkdir -p /shared/uploads
282 chown -R discourse:www-data /shared/uploads
284 if [[ ! -d /shared/backups ]]; then
285 mkdir -p /shared/backups
286 chown -R discourse:www-data /shared/backups
289 rm -rf /shared/tmp/{backups,restores}
290 mkdir -p /shared/tmp/{backups,restores}
291 chown -R discourse:www-data /shared/tmp/{backups,restores}
293 path: /etc/runit/1.d/01-cleanup-web-pids
297 /bin/rm -f /var/www/discourse/tmp/pids/*.pid
298 # change login directory to Discourse home
300 path: /root/.bash_profile
306 path: /usr/local/etc/ImageMagick-7/policy.xml
308 <?xml version="1.0" encoding="UTF-8"?>
309 <!DOCTYPE policymap [
310 <!ELEMENT policymap (policy)+>
311 <!ATTLIST policymap xmlns CDATA #FIXED ''>
312 <!ELEMENT policy EMPTY>
313 <!ATTLIST policy xmlns CDATA #FIXED '' domain NMTOKEN #REQUIRED
314 name NMTOKEN #IMPLIED pattern CDATA #IMPLIED rights NMTOKEN #IMPLIED
315 stealth NMTOKEN #IMPLIED value CDATA #IMPLIED>
318 Configure ImageMagick policies.
320 Domains include system, delegate, coder, filter, path, or resource.
322 Rights include none, read, write, execute and all. Use | to combine them,
323 for example: "read | write" to permit read from, or write to, a path.
325 Use a glob expression as a pattern.
327 Suppose we do not want users to process MPEG video images:
329 <policy domain="delegate" rights="none" pattern="mpeg:decode" />
331 Here we do not want users reading images from HTTP:
333 <policy domain="coder" rights="none" pattern="HTTP" />
335 The /repository file system is restricted to read only. We use a glob
336 expression to match all paths that start with /repository:
338 <policy domain="path" rights="read" pattern="/repository/*" />
340 Lets prevent users from executing any image filters:
342 <policy domain="filter" rights="none" pattern="*" />
344 Any large image is cached to disk rather than memory:
346 <policy domain="resource" name="area" value="1GP"/>
348 Define arguments for the memory, map, area, width, height and disk resources
349 with SI prefixes (.e.g 100MB). In addition, resource policies are maximums
350 for each instance of ImageMagick (e.g. policy memory limit 1GB, -limit 2GB
351 exceeds policy maximum so memory limit is 1GB).
353 Rules are processed in order. Here we want to restrict ImageMagick to only
354 read or write a small subset of proven web-safe image types:
356 <policy domain="delegate" rights="none" pattern="*" />
357 <policy domain="filter" rights="none" pattern="*" />
358 <policy domain="coder" rights="none" pattern="*" />
359 <policy domain="coder" rights="read|write" pattern="{GIF,JPEG,PNG,WEBP}" />
362 <!-- <policy domain="system" name="shred" value="2"/> -->
363 <!-- <policy domain="system" name="precision" value="6"/> -->
364 <!-- <policy domain="system" name="memory-map" value="anonymous"/> -->
365 <!-- <policy domain="system" name="max-memory-request" value="256MiB"/> -->
366 <!-- <policy domain="resource" name="temporary-path" value="/tmp"/> -->
367 <policy domain="resource" name="memory" value="1GiB"/>
368 <policy domain="resource" name="map" value="2GiB"/>
369 <policy domain="resource" name="width" value="64KP"/>
370 <policy domain="resource" name="height" value="64KP"/>
371 <!-- <policy domain="resource" name="list-length" value="128"/> -->
372 <policy domain="resource" name="area" value="4GP"/>
373 <policy domain="resource" name="disk" value="8GiB"/>
374 <!-- <policy domain="resource" name="file" value="768"/> -->
375 <!-- <policy domain="resource" name="thread" value="4"/> -->
376 <!-- <policy domain="resource" name="throttle" value="0"/> -->
377 <!-- <policy domain="resource" name="time" value="3600"/> -->
378 <!-- <policy domain="coder" rights="none" pattern="MVG" /> -->
379 <policy domain="module" rights="none" pattern="{PS,PS2,PS3,EPS,PDF,XPS}" />
380 <!-- <policy domain="delegate" rights="none" pattern="HTTPS" /> -->
381 <!-- <policy domain="path" rights="none" pattern="@*" /> -->
382 <!-- <policy domain="cache" name="memory-map" value="anonymous"/> -->
383 <!-- <policy domain="cache" name="synchronize" value="True"/> -->
384 <!-- <policy domain="cache" name="shared-secret" value="passphrase" stealth="true"/> -->