2 DIR
="$( cd "$
( dirname "${BASH_SOURCE[0]}" )" && pwd )"
6 ## Make sure only root can run our script
9 if [[ $EUID -ne 0 ]]; then
10 echo "This script must be run as root. Please sudo or log in as root first." 1>&2
16 ## Check whether a connection to HOSTNAME ($1) on PORT ($2) is possible
21 VERIFY
=`date +%s | sha256sum | base64 | head -c 20`
22 echo -e "HTTP/1.1 200 OK\n\n $VERIFY" | nc
-w 4 -l -p $PORT >/dev
/null
2>&1 &
23 if curl
--proto =http
-s $HOST:$PORT --connect-timeout 3 |
grep $VERIFY >/dev
/null
2>&1
27 curl
--proto =http
-s localhost
:$PORT >/dev
/null
2>&1
35 echo Checking your domain name . . .
36 if connect_to_port
$HOST 443
39 echo "Connection to $HOST succeeded."
41 echo WARNING
:: This server does not appear to be accessible
at $HOST:443.
43 if connect_to_port
$HOST 80
45 echo A connection to port
80 succeeds
, however.
46 echo This suggests that your DNS settings are correct
,
47 echo but something is keeping traffic to port
443 from getting to your server.
48 echo Check your networking configuration to see that connections to port
443 are allowed.
50 echo "A connection to http://$HOST (port 80) also fails."
52 echo This suggests that
$HOST resolves to the wrong IP address
53 echo or that traffic is not being routed to your server.
56 echo Google
: \"open ports YOUR CLOUD SERVICE
\" for information
for resolving this problem.
58 echo You should probably answer
\"n
\" at the next prompt and disable Let
\'s Encrypt.
60 echo This
test might not work
for all situations
,
61 echo so
if you can access Discourse
at http
://$HOST, you might try anyway.
69 check_and_install_docker
() {
70 docker_path
=`which docker.io || which docker`
71 if [ -z $docker_path ]; then
72 read -p "Docker not installed. Enter to install from https://get.docker.com/ or Ctrl+C to exit"
73 curl https
://get.docker.com
/ | sh
75 docker_path
=`which docker.io || which docker`
76 if [ -z $docker_path ]; then
77 echo Docker
install failed. Quitting.
83 ## What are we running on
90 ## OS X available memory
93 echo `free -m | awk '/Mem:/ {print $2}'`
97 ## Linux available memory
99 check_linux_memory
() {
100 echo `free -g --si | awk ' /Mem:/ {print $2} '`
104 ## Do we have enough memory and disk space for Discourse?
106 check_disk_and_memory
() {
110 if [ "$os_type" == "Darwin" ]; then
111 avail_mem
=$
(check_osx_memory
)
113 avail_mem
=$
(check_linux_memory
)
116 if [ "$avail_mem" -lt 1 ]; then
117 echo "WARNING: Discourse requires 1GB RAM to run. This system does not appear"
118 echo "to have sufficient memory."
120 echo "Your site may not work properly, or future upgrades of Discourse may not"
121 echo "complete successfully."
125 if [ "$avail_mem" -le 2 ]; then
126 total_swap
=`free -g --si | awk ' /Swap:/ {print $2} '`
128 if [ "$total_swap" -lt 2 ]; then
129 echo "WARNING: Discourse requires at least 2GB of swap when running with 2GB of RAM"
130 echo "or less. This system does not appear to have sufficient swap space."
132 echo "Without sufficient swap space, your site may not work properly, and future"
133 echo "upgrades of Discourse may not complete successfully."
135 echo "Ctrl+C to exit or wait 5 seconds to have a 2GB swapfile created."
139 ## derived from https://meta.discourse.org/t/13880
141 install -o root
-g root
-m 0600 /dev
/null
/swapfile
142 dd if=/dev
/zero of
=/swapfile bs
=1k count
=2048k
145 echo "/swapfile swap swap auto 0 0" |
tee -a /etc
/fstab
146 sysctl
-w vm.swappiness
=10
147 echo vm.swappiness
= 10 |
tee -a /etc
/sysctl.conf
149 total_swap
=`free -g --si | awk ' /Swap:/ {print $2} '`
150 if [ "$total_swap" -lt 2 ]; then
151 echo "Failed to create swap: are you root? Are you running on real hardware, or a fully virtualized server?"
159 free_disk
="$(df /var | tail -n 1 | awk '{print $4}')"
160 if [ "$free_disk" -lt 5000 ]; then
161 echo "WARNING: Discourse requires at least 5GB free disk space. This system"
162 echo "does not appear to have sufficient disk space."
164 echo "Insufficient disk space may result in problems running your site, and"
165 echo "may not even allow Discourse installation to complete successfully."
167 echo "Please free up some space, or expand your disk, before continuing."
169 echo "Run \`apt-get autoremove && apt-get autoclean\` to clean up unused"
170 echo "packages and \`./launcher cleanup\` to remove stale Docker containers."
178 ## If we have lots of RAM or lots of CPUs, bump up the defaults to scale better
180 scale_ram_and_cpu
() {
182 local changelog
=/tmp
/changelog.
$PPID
183 # grab info about total system ram and physical (NOT LOGICAL!) CPU cores
187 if [ "$os_type" == "Darwin" ]; then
188 avail_gb
=$
(check_osx_memory
)
189 avail_cores
=`sysctl hw.ncpu | awk '/hw.ncpu:/ {print $2}'`
191 avail_gb
=$
(check_linux_memory
)
192 avail_cores
=$
((`awk '/cpu cores/ {print $4;exit}' /proc/cpuinfo`*`sort /proc/cpuinfo | uniq | grep -c "physical id"`))
194 echo "Found ${avail_gb}GB of memory and $avail_cores physical CPU cores"
196 # db_shared_buffers: 128MB for 1GB, 256MB for 2GB, or 256MB * GB, max 4096MB
197 if [ "$avail_gb" -eq "1" ]
199 db_shared_buffers
=128
201 if [ "$avail_gb" -eq "2" ]
203 db_shared_buffers
=256
205 db_shared_buffers
=$
(( 256 * $avail_gb ))
208 db_shared_buffers
=$
(( db_shared_buffers
< 4096 ? db_shared_buffers
: 4096 ))
210 sed -i -e "s/^ #\?db_shared_buffers:.*/ db_shared_buffers: \"${db_shared_buffers}MB\"/w $changelog" $data_file
213 echo "setting db_shared_buffers = ${db_shared_buffers}MB"
217 # UNICORN_WORKERS: 2 * GB for 2GB or less, or 2 * CPU, max 8
218 if [ "$avail_gb" -le "2" ]
220 unicorn_workers
=$
(( 2 * $avail_gb ))
222 unicorn_workers
=$
(( 2 * $avail_cores ))
224 unicorn_workers
=$
(( unicorn_workers
< 8 ? unicorn_workers
: 8 ))
226 sed -i -e "s/^ #\?UNICORN_WORKERS:.*/ UNICORN_WORKERS: ${unicorn_workers}/w $changelog" $web_file
229 echo "setting UNICORN_WORKERS = ${unicorn_workers}"
233 echo $data_file memory parameters updated.
238 ## standard http / https ports must not be occupied
243 echo "Ports 80 and 443 are free for use"
248 ## check a port to see if it is already in use
252 local valid
=$
(netstat
-tln |
awk '{print $4}' |
grep ":${1}\$")
254 if [ -n "$valid" ]; then
255 echo "Port ${1} appears to already be in use."
257 echo "This will show you what command is using port ${1}"
258 lsof
-i tcp
:${1} -s tcp
:listen
260 echo "If you are trying to run Discourse simultaneously with another web"
261 echo "server like Apache or nginx, you will need to bind to a different port"
263 echo "See https://meta.discourse.org/t/17247"
265 echo "If you are reconfiguring an already-configured Discourse, use "
267 echo "./launcher stop app"
269 echo "to stop Discourse before you reconfigure it and try again."
275 ## read a variable from the config file
278 config_line
=`egrep "^ #?$1:" $web_file`
279 read_config_result
=`echo $config_line | awk --field-separator=":" '{print $2}'`
280 read_config_result
=`echo $read_config_result | sed "s/^\([\"']\)\(.*\)\1\$/\2/g"`
286 ## prompt user for typical Discourse config file values
288 ask_user_for_config
() {
290 # NOTE: Defaults now come from standalone.yml
292 local changelog
=/tmp
/changelog.
$PPID
293 read_config
"DISCOURSE_SMTP_ADDRESS"
294 local smtp_address
=$read_config_result
295 # NOTE: if there are spaces between emails, this breaks, but a human should be paying attention
296 read_config
"DISCOURSE_DEVELOPER_EMAILS"
297 local developer_emails
=$read_config_result
298 read_config
"DISCOURSE_SMTP_PASSWORD"
299 local smtp_password
=$read_config_result
300 read_config
"DISCOURSE_SMTP_PORT"
301 local smtp_port
=$read_config_result
302 read_config
"DISCOURSE_SMTP_USER_NAME"
303 local smtp_user_name
=$read_config_result
304 if [ "$smtp_password" = "pa$$word" ]
308 read_config
"LETSENCRYPT_ACCOUNT_EMAIL"
309 local letsencrypt_account_email
=$read_config_result
310 if [ -z $letsencrypt_account_email ]
312 letsencrypt_account_email
="me@example.com"
314 if [ "$letsencrypt_account_email" = "me@example.com" ]
316 local letsencrypt_status
="ENTER to skip"
318 local letsencrypt_status
="Enter 'OFF' to disable."
321 read_config
"DISCOURSE_HOSTNAME"
322 hostname
=$read_config_result
330 while [[ "$config_ok" == "n" ]]
332 if [ ! -z $hostname ]
334 read -p "Hostname for your Discourse? [$hostname]: " new_value
335 if [ ! -z "$new_value" ]
337 hostname
="$new_value"
339 if [[ $hostname =~ ^
[0-9]{1,3}\.
[0-9]{1,3}\.
[0-9]{1,3}\.
[0-9]{1,3}$
]]
342 echo "Discourse requires a DNS hostname. IP addresses are unsupported and will not work."
344 hostname
="discourse.example.com"
348 if [ ! -z $developer_emails ]
350 read -p "Email address for admin account(s)? [$developer_emails]: " new_value
351 if [ ! -z "$new_value" ]
353 developer_emails
="$new_value"
357 if [ ! -z $smtp_address ]
359 read -p "SMTP server address? [$smtp_address]: " new_value
360 if [ ! -z "$new_value" ]
362 smtp_address
="$new_value"
366 if [ ! -z $smtp_port ]
368 read -p "SMTP port? [$smtp_port]: " new_value
369 if [ ! -z "$new_value" ]
371 smtp_port
="$new_value"
376 ## automatically set correct user name based on common mail providers unless it's been set
378 if [ "$smtp_user_name" == "user@example.com" ]
380 if [ "$smtp_address" == "smtp.sparkpostmail.com" ]
382 smtp_user_name
="SMTP_Injection"
384 if [ "$smtp_address" == "smtp.sendgrid.net" ]
386 smtp_user_name
="apikey"
388 if [ "$smtp_address" == "smtp.mailgun.org" ]
390 smtp_user_name
="postmaster@$hostname"
394 if [ ! -z "$smtp_user_name" ]
396 read -p "SMTP user name? [$smtp_user_name]: " new_value
397 if [ ! -z "$new_value" ]
399 smtp_user_name
="$new_value"
403 read -p "SMTP password? [$smtp_password]: " new_value
404 if [ ! -z "$new_value" ]
406 smtp_password
="$new_value"
409 if [ ! -z $letsencrypt_account_email ]
411 read -p "Let's Encrypt account email? ($letsencrypt_status) [$letsencrypt_account_email]: " new_value
412 if [ ! -z "$new_value" ]
414 letsencrypt_account_email
="$new_value"
415 if [ "${new_value,,}" = "off" ]
417 letsencrypt_status
="ENTER to skip"
419 letsencrypt_status
="Enter 'OFF' to disable."
424 if [ "$letsencrypt_status" == "Enter 'OFF' to disable." ]
426 check_IP_match
$hostname
429 echo -e "\nDoes this look right?\n"
430 echo "Hostname : $hostname"
431 echo "Email : $developer_emails"
432 echo "SMTP address : $smtp_address"
433 echo "SMTP port : $smtp_port"
434 echo "SMTP username : $smtp_user_name"
435 echo "SMTP password : $smtp_password"
437 if [ "$letsencrypt_status" == "Enter 'OFF' to disable." ]
439 echo "Let's Encrypt : $letsencrypt_account_email"
444 read -p "ENTER to continue, 'n' to try again, Ctrl+C to exit: " config_ok
447 sed -i -e "s/^ DISCOURSE_HOSTNAME:.*/ DISCOURSE_HOSTNAME: $hostname/w $changelog" $web_file
452 echo "DISCOURSE_HOSTNAME change failed."
456 sed -i -e "s/^ DISCOURSE_DEVELOPER_EMAILS:.*/ DISCOURSE_DEVELOPER_EMAILS: \'$developer_emails\'/w $changelog" $web_file
461 echo "DISCOURSE_DEVELOPER_EMAILS change failed."
465 sed -i -e "s/^ DISCOURSE_SMTP_ADDRESS:.*/ DISCOURSE_SMTP_ADDRESS: $smtp_address/w $changelog" $web_file
470 echo "DISCOURSE_SMTP_ADDRESS change failed."
474 sed -i -e "s/^ #\?DISCOURSE_SMTP_PORT:.*/ DISCOURSE_SMTP_PORT: $smtp_port/w $changelog" $web_file
479 echo "DISCOURSE_SMTP_PORT change failed."
483 sed -i -e "s/^ #\?DISCOURSE_SMTP_USER_NAME:.*/ DISCOURSE_SMTP_USER_NAME: $smtp_user_name/w $changelog" $web_file
488 echo "DISCOURSE_SMTP_USER_NAME change failed."
492 if [[ "$smtp_password" == *"\""* ]]
495 echo "========================================"
497 echo "Your password contains a quote (\")"
498 echo "Your SMTP Password will not be set. You will need to edit app.yml to enter it."
499 echo "========================================"
503 if [[ "$smtp_password" == *"$SLASH"* ]]
505 if [[ "$smtp_password" == *"$SLASH"* ]]
508 if [[ "$smtp_password" == *"$SLASH"* ]]
511 echo "========================================"
513 echo "Your password contains all available delimiters (+, |, and Q). "
514 echo "Your SMTP Password will not be set. You will need to edit app.yml to enter it."
515 echo "========================================"
521 if [[ "$SLASH" != "BROKEN" ]]
523 sed -i -e "s${SLASH}^ #\?DISCOURSE_SMTP_PASSWORD:.*${SLASH} DISCOURSE_SMTP_PASSWORD: \"${smtp_password}\"${SLASH}w $changelog" $web_file
529 echo "DISCOURSE_SMTP_PASSWORD change failed."
533 if [ "$letsencrypt_status" = "ENTER to skip" ]
535 local src
='^ #\?- "templates\/web.ssl.template.yml"'
536 local dst
=' #\- "templates\/web.ssl.template.yml"'
537 sed -i -e "s/$src/$dst/w $changelog" $web_file
538 if [ ! -s $changelog ]
541 echo "web.ssl.template.yml NOT DISABLED--Are you using a non-standard template?"
543 local src
='^ #\?- "templates\/web.letsencrypt.ssl.template.yml"'
544 local dst
=' #- "templates\/web.letsencrypt.ssl.template.yml"'
546 sed -i -e "s/$src/$dst/w $changelog" $web_file
547 if [ ! -s $changelog ]
550 echo "web.ssl.template.yml NOT DISABLED--Are you using a non-standard template?"
552 else # enable let's encrypt
553 echo "Let's Encrypt will be enabled for $letsencrypt_account_email"
554 sed -i -e "s/^ #\?LETSENCRYPT_ACCOUNT_EMAIL:.*/ LETSENCRYPT_ACCOUNT_EMAIL: $letsencrypt_account_email/w $changelog" $web_file
559 echo "LETSENCRYPT_ACCOUNT_EMAIL change failed."
562 local src
='^ #\?- "templates\/web.ssl.template.yml"'
563 local dst
=' \- "templates\/web.ssl.template.yml"'
564 sed -i -e "s/$src/$dst/w $changelog" $web_file
567 echo "web.ssl.template.yml enabled"
570 echo "web.ssl.template.yml NOT ENABLED--was it on already?"
572 local src
='^ #\?- "templates\/web.letsencrypt.ssl.template.yml"'
573 local dst
=' - "templates\/web.letsencrypt.ssl.template.yml"'
575 sed -i -e "s/$src/$dst/w $changelog" $web_file
578 echo "letsencrypt.ssl.template.yml enabled"
581 echo "letsencrypt.ssl.template.yml NOT ENABLED -- was it on already?"
585 if [ "$update_ok" == "y" ]
587 echo -e "\nConfiguration file at $config_file updated successfully!\n"
589 echo -e "\nUnfortunately, there was an error changing $config_file\n"
590 echo -d "This may happen if you have made unexpected changes."
596 ## is our config file valid? Does it have the required fields set?
602 for x
in DISCOURSE_SMTP_ADDRESS DISCOURSE_SMTP_USER_NAME DISCOURSE_SMTP_PASSWORD \
603 DISCOURSE_DEVELOPER_EMAILS DISCOURSE_HOSTNAME
605 config_line
=`grep "^ $x:" $web_file`
607 local default
="example.com"
611 if [[ "$config_line" = *"$default"* ]]
613 echo "$x left at incorrect default of example.com"
616 config_val
=`echo $config_line | awk '{print $2}'`
617 if [ -z $config_val ]
619 echo "$x was left blank"
623 echo "$x not present"
628 if [ "$valid_config" != "y" ]; then
629 echo -e "\nSorry, these $web_file settings aren't valid -- can't continue!"
630 echo "If you have unusual requirements, edit $web_file and then: "
631 echo "./launcher bootstrap $app_name"
638 ## template file names
641 if [ "$1" == "2container" ]
645 web_template
=samples
/web_only.yml
646 data_template
=samples
/data.yml
647 web_file
=containers
/$app_name.yml
648 data_file
=containers
/$data_name.yml
652 web_template
=samples
/standalone.yml
654 web_file
=containers
/$app_name.yml
655 data_file
=containers
/$app_name.yml
657 changelog
=/tmp
/changelog
660 ## Check requirements before creating a copy of a config file we won't edit
663 check_and_install_docker
664 check_disk_and_memory
666 if [ -a "$web_file" ]
668 echo "The configuration file $web_file already exists!"
670 echo ". . . reconfiguring . . ."
673 DATE
=`date +"%Y-%m-%d-%H%M%S"`
674 BACKUP
=$app_name.yml.
$DATE.bak
675 echo Saving old
file as
$BACKUP
676 cp $web_file containers
/$BACKUP
677 echo "Stopping existing container in 5 seconds or Control-C to cancel."
683 cp -v $web_template $web_file
684 if [ "$data_name" == "data" ]
686 echo "--------------------------------------------------"
687 echo "This two container setup is currently unsupported. Use at your own risk!"
688 echo "--------------------------------------------------"
689 DISCOURSE_DB_PASSWORD
=`date +%s | sha256sum | base64 | head -c 20`
691 sed -i -e "s/DISCOURSE_DB_PASSWORD: SOME_SECRET/DISCOURSE_DB_PASSWORD: $DISCOURSE_DB_PASSWORD/w $changelog" $web_file
696 echo "Problem changing DISCOURSE_DB_PASSWORD" in $web_file
699 cp -v $data_template $data_file
701 sed -i -e "s/password ${quote}SOME_SECRET${quote}/password '$DISCOURSE_DB_PASSWORD'/w $changelog" $data_file
706 echo "Problem changing DISCOURSE_DB_PASSWORD" in $data_file
716 ## if we reach this point without exiting, OK to proceed
717 ## rebuild won't fail if there's nothing to rebuild and does the restart
719 echo "Updates successful. Rebuilding in 5 seconds."
720 sleep 5 # Just a chance to ^C in case they were too fast on the draw
721 if [ "$data_name" == "$app_name" ]
723 echo Building
$app_name
724 .
/launcher rebuild
$app_name
726 echo Building
$data_name now . . .
727 .
/launcher rebuild
$data_name
728 echo Building
$app_name now . . .
729 .
/launcher rebuild
$app_name