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 if ! [ -x "$(command -v nc)" ]; then
23 echo "In order to check the connection to $HOST:$PORT we need to open a socket using netcat."
24 echo However netcat is not installed on your system. You can
continue without this check
25 echo or abort the setup
, install netcat and try again.
27 read -p "Would you like to continue without this check? [yn] " yn
31 *) echo "Please answer y or n." ;;
35 echo -e "HTTP/1.1 200 OK\n\n $VERIFY" | nc
-w 4 -l -p $PORT >/dev
/null
2>&1 &
36 if curl
--proto =http
-s $HOST:$PORT --connect-timeout 3 |
grep $VERIFY >/dev
/null
2>&1; then
39 curl
--proto =http
-s localhost
:$PORT >/dev
/null
2>&1
48 echo Checking your domain name . . .
49 connect_to_port
$HOST 443; ec
=$?
52 echo "Connection to $HOST succeeded."
55 echo "WARNING:: This server does not appear to be accessible at $HOST:443."
57 if connect_to_port
$HOST 80; then
58 echo A connection to port
80 succeeds
, however.
59 echo This suggests that your DNS settings are correct
,
60 echo but something is keeping traffic to port
443 from getting to your server.
61 echo Check your networking configuration to see that connections to port
443 are allowed.
63 echo "A connection to http://$HOST (port 80) also fails."
65 echo "This suggests that $HOST resolves to the wrong IP address"
66 echo or that traffic is not being routed to your server.
69 echo Google
: \"open ports YOUR CLOUD SERVICE
\" for information
for resolving this problem.
71 echo You should probably answer
\"n
\" at the next prompt and disable Let
\'s Encrypt.
73 echo This
test might not work
for all situations
,
74 echo "so if you can access Discourse at http://$HOST, you might try anyway."
78 echo "Continuing without port check."
86 check_and_install_docker
() {
87 docker_path
=`which docker.io || which docker`
88 if [ -z $docker_path ]; then
89 read -p "Docker not installed. Enter to install from https://get.docker.com/ or Ctrl+C to exit"
90 curl https
://get.docker.com
/ | sh
92 docker_path
=`which docker.io || which docker`
93 if [ -z $docker_path ]; then
94 echo Docker
install failed. Quitting.
100 ## What are we running on
107 ## OS X available memory
110 echo `free -m | awk '/Mem:/ {print $2}'`
114 ## Linux available memory
116 check_linux_memory
() {
117 echo `free -g --si | awk ' /Mem:/ {print $2} '`
121 ## Do we have enough memory and disk space for Discourse?
123 check_disk_and_memory
() {
127 if [ "$os_type" == "Darwin" ]; then
128 avail_mem
=$
(check_osx_memory
)
130 avail_mem
=$
(check_linux_memory
)
133 if [ "$avail_mem" -lt 1 ]; then
134 echo "WARNING: Discourse requires 1GB RAM to run. This system does not appear"
135 echo "to have sufficient memory."
137 echo "Your site may not work properly, or future upgrades of Discourse may not"
138 echo "complete successfully."
142 if [ "$avail_mem" -le 2 ]; then
143 total_swap
=`free -g --si | awk ' /Swap:/ {print $2} '`
145 if [ "$total_swap" -lt 2 ]; then
146 echo "WARNING: Discourse requires at least 2GB of swap when running with 2GB of RAM"
147 echo "or less. This system does not appear to have sufficient swap space."
149 echo "Without sufficient swap space, your site may not work properly, and future"
150 echo "upgrades of Discourse may not complete successfully."
152 echo "Ctrl+C to exit or wait 5 seconds to have a 2GB swapfile created."
156 ## derived from https://meta.discourse.org/t/13880
158 install -o root
-g root
-m 0600 /dev
/null
/swapfile
159 fallocate
-l 2G
/swapfile
162 echo "/swapfile swap swap auto 0 0" |
tee -a /etc
/fstab
163 sysctl
-w vm.swappiness
=10
164 echo 'vm.swappiness = 10' > /etc
/sysctl.d
/30-discourse-swap.conf
166 total_swap
=`free -g --si | awk ' /Swap:/ {print $2} '`
167 if [ "$total_swap" -lt 2 ]; then
168 echo "Failed to create swap: are you root? Are you running on real hardware, or a fully virtualized server?"
176 free_disk
="$(df /var | tail -n 1 | awk '{print $4}')"
177 if [ "$free_disk" -lt 5000 ]; then
178 echo "WARNING: Discourse requires at least 5GB free disk space. This system"
179 echo "does not appear to have sufficient disk space."
181 echo "Insufficient disk space may result in problems running your site, and"
182 echo "may not even allow Discourse installation to complete successfully."
184 echo "Please free up some space, or expand your disk, before continuing."
186 echo "Run \`apt-get autoremove && apt-get autoclean\` to clean up unused"
187 echo "packages and \`./launcher cleanup\` to remove stale Docker containers."
195 ## If we have lots of RAM or lots of CPUs, bump up the defaults to scale better
197 scale_ram_and_cpu
() {
199 local changelog
=/tmp
/changelog.
$PPID
200 # grab info about total system ram and physical (NOT LOGICAL!) CPU cores
204 if [ "$os_type" == "Darwin" ]; then
205 avail_gb
=$
(check_osx_memory
)
206 avail_cores
=`sysctl hw.ncpu | awk '/hw.ncpu:/ {print $2}'`
208 avail_gb
=$
(check_linux_memory
)
209 avail_cores
=$
((`awk '/cpu cores/ {print $4;exit}' /proc/cpuinfo`*`sort /proc/cpuinfo | uniq | grep -c "physical id"`))
211 echo "Found ${avail_gb}GB of memory and $avail_cores physical CPU cores"
213 # db_shared_buffers: 128MB for 1GB, 256MB for 2GB, or 256MB * GB, max 4096MB
214 if [ "$avail_gb" -eq "1" ]
216 db_shared_buffers
=128
218 if [ "$avail_gb" -eq "2" ]
220 db_shared_buffers
=256
222 db_shared_buffers
=$
(( 256 * $avail_gb ))
225 db_shared_buffers
=$
(( db_shared_buffers
< 4096 ? db_shared_buffers
: 4096 ))
227 sed -i -e "s/^ #\?db_shared_buffers:.*/ db_shared_buffers: \"${db_shared_buffers}MB\"/w $changelog" $data_file
230 echo "setting db_shared_buffers = ${db_shared_buffers}MB"
234 # UNICORN_WORKERS: 2 * GB for 2GB or less, or 2 * CPU, max 8
235 if [ "$avail_gb" -le "2" ]
237 unicorn_workers
=$
(( 2 * $avail_gb ))
239 unicorn_workers
=$
(( 2 * $avail_cores ))
241 unicorn_workers
=$
(( unicorn_workers
< 8 ? unicorn_workers
: 8 ))
243 sed -i -e "s/^ #\?UNICORN_WORKERS:.*/ UNICORN_WORKERS: ${unicorn_workers}/w $changelog" $web_file
246 echo "setting UNICORN_WORKERS = ${unicorn_workers}"
250 echo $data_file memory parameters updated.
255 ## standard http / https ports must not be occupied
260 echo "Ports 80 and 443 are free for use"
265 ## check a port to see if it is already in use
269 local valid
=$
(netstat
-tln |
awk '{print $4}' |
grep ":${1}\$")
271 if [ -n "$valid" ]; then
272 echo "Port ${1} appears to already be in use."
274 echo "This will show you what command is using port ${1}"
275 lsof
-i tcp
:${1} -s tcp
:listen
277 echo "If you are trying to run Discourse simultaneously with another web"
278 echo "server like Apache or nginx, you will need to bind to a different port"
280 echo "See https://meta.discourse.org/t/17247"
282 echo "If you are reconfiguring an already-configured Discourse, use "
284 echo "./launcher stop app"
286 echo "to stop Discourse before you reconfigure it and try again."
292 ## read a variable from the config file
295 config_line
=`egrep "^ #?$1:" $web_file`
296 read_config_result
=`echo $config_line | awk -F":" '{print $2}'`
297 read_config_result
=`echo $read_config_result | sed "s/^\([\"']\)\(.*\)\1\$/\2/g"`
301 config_line
=`egrep "^ #?$1:" samples/standalone.yml`
302 read_default_result
=`echo $config_line | awk -F":" '{print $2}'`
303 read_default_result
=`echo $read_config_result | sed "s/^\([\"']\)\(.*\)\1\$/\2/g"`
307 ## prompt user for typical Discourse config file values
309 ask_user_for_config
() {
311 # NOTE: Defaults now come from standalone.yml
313 local changelog
=/tmp
/changelog.
$PPID
314 read_config
"DISCOURSE_SMTP_ADDRESS"
315 local smtp_address
=$read_config_result
316 # NOTE: if there are spaces between emails, this breaks, but a human should be paying attention
317 read_config
"DISCOURSE_DEVELOPER_EMAILS"
318 local developer_emails
=$read_config_result
319 read_config
"DISCOURSE_SMTP_PASSWORD"
320 local smtp_password
=$read_config_result
321 read_config
"DISCOURSE_SMTP_PORT"
322 local smtp_port
=$read_config_result
323 read_config
"DISCOURSE_SMTP_USER_NAME"
324 local smtp_user_name
=$read_config_result
325 if [ "$smtp_password" = "pa$$word" ]
329 read_config
"LETSENCRYPT_ACCOUNT_EMAIL"
330 local letsencrypt_account_email
=$read_config_result
331 if [ -z $letsencrypt_account_email ]
333 letsencrypt_account_email
="me@example.com"
335 if [ "$letsencrypt_account_email" = "me@example.com" ]
337 local letsencrypt_status
="ENTER to skip"
339 local letsencrypt_status
="Enter 'OFF' to disable."
342 read_config
"DISCOURSE_HOSTNAME"
343 hostname
=$read_config_result
351 while [[ "$config_ok" == "n" ]]
353 if [ ! -z "$hostname" ]
355 read -p "Hostname for your Discourse? [$hostname]: " new_value
356 if [ ! -z "$new_value" ]
358 hostname
="$new_value"
360 if [[ $hostname =~ ^
[0-9]{1,3}\.
[0-9]{1,3}\.
[0-9]{1,3}\.
[0-9]{1,3}$
]]
363 echo "Discourse requires a DNS hostname. IP addresses are unsupported and will not work."
365 hostname
="discourse.example.com"
369 if [ ! -z "$developer_emails" ]
371 read -p "Email address for admin account(s)? [$developer_emails]: " new_value
372 if [ ! -z "$new_value" ]
374 developer_emails
="$new_value"
378 if [ ! -z "$smtp_address" ]
380 read -p "SMTP server address? [$smtp_address]: " new_value
381 if [ ! -z "$new_value" ]
383 smtp_address
="$new_value"
387 if [ ! -z "$smtp_port" ]
389 read -p "SMTP port? [$smtp_port]: " new_value
390 if [ ! -z "$new_value" ]
392 smtp_port
="$new_value"
397 ## automatically set correct user name based on common mail providers unless it's been set
399 if [ "$smtp_user_name" == "user@example.com" ]
401 if [ "$smtp_address" == "smtp.sparkpostmail.com" ]
403 smtp_user_name
="SMTP_Injection"
405 if [ "$smtp_address" == "smtp.sendgrid.net" ]
407 smtp_user_name
="apikey"
409 if [ "$smtp_address" == "smtp.mailgun.org" ]
411 smtp_user_name
="postmaster@$hostname"
415 if [ ! -z "$smtp_user_name" ]
417 read -p "SMTP user name? [$smtp_user_name]: " new_value
418 if [ ! -z "$new_value" ]
420 smtp_user_name
="$new_value"
424 read -p "SMTP password? [$smtp_password]: " new_value
425 if [ ! -z "$new_value" ]
427 smtp_password
="$new_value"
430 if [ ! -z $letsencrypt_account_email ]
432 read -p "Optional email address for setting up Let's Encrypt? ($letsencrypt_status) [$letsencrypt_account_email]: " new_value
433 if [ ! -z "$new_value" ]
435 letsencrypt_account_email
="$new_value"
436 if [ "${new_value,,}" = "off" ]
438 letsencrypt_status
="ENTER to skip"
440 letsencrypt_status
="Enter 'OFF' to disable."
445 if [ "$letsencrypt_status" == "Enter 'OFF' to disable." ]
447 check_IP_match
$hostname
450 echo -e "\nDoes this look right?\n"
451 echo "Hostname : $hostname"
452 echo "Email : $developer_emails"
453 echo "SMTP address : $smtp_address"
454 echo "SMTP port : $smtp_port"
455 echo "SMTP username : $smtp_user_name"
456 echo "SMTP password : $smtp_password"
458 if [ "$letsencrypt_status" == "Enter 'OFF' to disable." ]
460 echo "Let's Encrypt : $letsencrypt_account_email"
465 read -p "ENTER to continue, 'n' to try again, Ctrl+C to exit: " config_ok
468 sed -i -e "s/^ DISCOURSE_HOSTNAME:.*/ DISCOURSE_HOSTNAME: $hostname/w $changelog" $web_file
473 echo "DISCOURSE_HOSTNAME change failed."
477 sed -i -e "s/^ DISCOURSE_DEVELOPER_EMAILS:.*/ DISCOURSE_DEVELOPER_EMAILS: \'$developer_emails\'/w $changelog" $web_file
482 echo "DISCOURSE_DEVELOPER_EMAILS change failed."
486 sed -i -e "s/^ DISCOURSE_SMTP_ADDRESS:.*/ DISCOURSE_SMTP_ADDRESS: $smtp_address/w $changelog" $web_file
491 echo "DISCOURSE_SMTP_ADDRESS change failed."
495 sed -i -e "s/^ #\?DISCOURSE_SMTP_PORT:.*/ DISCOURSE_SMTP_PORT: $smtp_port/w $changelog" $web_file
500 echo "DISCOURSE_SMTP_PORT change failed."
504 sed -i -e "s/^ #\?DISCOURSE_SMTP_USER_NAME:.*/ DISCOURSE_SMTP_USER_NAME: $smtp_user_name/w $changelog" $web_file
509 echo "DISCOURSE_SMTP_USER_NAME change failed."
513 if [[ "$smtp_password" == *"\""* ]]
516 echo "========================================"
518 echo "Your password contains a quote (\")"
519 echo "Your SMTP Password will not be set. You will need to edit app.yml to enter it."
520 echo "========================================"
524 if [[ "$smtp_password" == *"$SLASH"* ]]
526 if [[ "$smtp_password" == *"$SLASH"* ]]
529 if [[ "$smtp_password" == *"$SLASH"* ]]
532 echo "========================================"
534 echo "Your password contains all available delimiters (+, |, and Q). "
535 echo "Your SMTP Password will not be set. You will need to edit app.yml to enter it."
536 echo "========================================"
542 if [[ "$SLASH" != "BROKEN" ]]
544 sed -i -e "s${SLASH}^ #\?DISCOURSE_SMTP_PASSWORD:.*${SLASH} DISCOURSE_SMTP_PASSWORD: \"${smtp_password}\"${SLASH}w $changelog" $web_file
550 echo "DISCOURSE_SMTP_PASSWORD change failed."
554 if [ "$letsencrypt_status" = "ENTER to skip" ]
556 local src
='^ #\?- "templates\/web.ssl.template.yml"'
557 local dst
=' #\- "templates\/web.ssl.template.yml"'
558 sed -i -e "s/$src/$dst/w $changelog" $web_file
559 if [ ! -s $changelog ]
562 echo "web.ssl.template.yml NOT DISABLED--Are you using a non-standard template?"
564 local src
='^ #\?- "templates\/web.letsencrypt.ssl.template.yml"'
565 local dst
=' #- "templates\/web.letsencrypt.ssl.template.yml"'
567 sed -i -e "s/$src/$dst/w $changelog" $web_file
568 if [ ! -s $changelog ]
571 echo "web.ssl.template.yml NOT DISABLED--Are you using a non-standard template?"
573 else # enable let's encrypt
574 echo "Let's Encrypt will be enabled for $letsencrypt_account_email"
575 sed -i -e "s/^ #\?LETSENCRYPT_ACCOUNT_EMAIL:.*/ LETSENCRYPT_ACCOUNT_EMAIL: $letsencrypt_account_email/w $changelog" $web_file
580 echo "LETSENCRYPT_ACCOUNT_EMAIL change failed."
583 local src
='^ #\?- "templates\/web.ssl.template.yml"'
584 local dst
=' \- "templates\/web.ssl.template.yml"'
585 sed -i -e "s/$src/$dst/w $changelog" $web_file
588 echo "web.ssl.template.yml enabled"
591 echo "web.ssl.template.yml NOT ENABLED--was it on already?"
593 local src
='^ #\?- "templates\/web.letsencrypt.ssl.template.yml"'
594 local dst
=' - "templates\/web.letsencrypt.ssl.template.yml"'
596 sed -i -e "s/$src/$dst/w $changelog" $web_file
599 echo "letsencrypt.ssl.template.yml enabled"
602 echo "letsencrypt.ssl.template.yml NOT ENABLED -- was it on already?"
606 if [ "$update_ok" == "y" ]
608 echo -e "\nConfiguration file at $config_file updated successfully!\n"
610 echo -e "\nUnfortunately, there was an error changing $config_file\n"
611 echo -d "This may happen if you have made unexpected changes."
617 ## is our config file valid? Does it have the required fields set?
623 for x
in DISCOURSE_SMTP_ADDRESS DISCOURSE_SMTP_USER_NAME DISCOURSE_SMTP_PASSWORD \
624 DISCOURSE_DEVELOPER_EMAILS DISCOURSE_HOSTNAME
627 local result
=$read_config_result
629 local default
=$read_default_result
631 if [ ! -z "$result" ]
633 if [[ "$config_line" = *"$default"* ]]
635 echo "$x left at incorrect default of $default"
638 config_val
=`echo $config_line | awk '{print $2}'`
639 if [ -z $config_val ]
641 echo "$x was not configured"
645 echo "$x not present"
650 if [ "$valid_config" != "y" ]; then
651 echo -e "\nSorry, these $web_file settings aren't valid -- can't continue!"
652 echo "If you have unusual requirements, edit $web_file and then: "
653 echo "./launcher bootstrap $app_name"
660 ## template file names
663 if [ "$1" == "2container" ]
667 web_template
=samples
/web_only.yml
668 data_template
=samples
/data.yml
669 web_file
=containers
/$app_name.yml
670 data_file
=containers
/$data_name.yml
674 web_template
=samples
/standalone.yml
676 web_file
=containers
/$app_name.yml
677 data_file
=containers
/$app_name.yml
679 changelog
=/tmp
/changelog
682 ## Check requirements before creating a copy of a config file we won't edit
685 check_and_install_docker
686 check_disk_and_memory
688 if [ -a "$web_file" ]
690 echo "The configuration file $web_file already exists!"
692 echo ". . . reconfiguring . . ."
695 DATE
=`date +"%Y-%m-%d-%H%M%S"`
696 BACKUP
=$app_name.yml.
$DATE.bak
697 echo Saving old
file as
$BACKUP
698 cp $web_file containers
/$BACKUP
699 echo "Stopping existing container in 5 seconds or Control-C to cancel."
705 cp -v $web_template $web_file
706 if [ "$data_name" == "data" ]
708 echo "--------------------------------------------------"
709 echo "This two container setup is currently unsupported. Use at your own risk!"
710 echo "--------------------------------------------------"
711 DISCOURSE_DB_PASSWORD
=`date +%s | sha256sum | base64 | head -c 20`
713 sed -i -e "s/DISCOURSE_DB_PASSWORD: SOME_SECRET/DISCOURSE_DB_PASSWORD: $DISCOURSE_DB_PASSWORD/w $changelog" $web_file
718 echo "Problem changing DISCOURSE_DB_PASSWORD" in $web_file
721 cp -v $data_template $data_file
723 sed -i -e "s/password ${quote}SOME_SECRET${quote}/password '$DISCOURSE_DB_PASSWORD'/w $changelog" $data_file
728 echo "Problem changing DISCOURSE_DB_PASSWORD" in $data_file
738 ## if we reach this point without exiting, OK to proceed
739 ## rebuild won't fail if there's nothing to rebuild and does the restart
741 echo "Updates successful. Rebuilding in 5 seconds."
742 sleep 5 # Just a chance to ^C in case they were too fast on the draw
743 if [ "$data_name" == "$app_name" ]
745 echo Building
$app_name
746 .
/launcher rebuild
$app_name
748 echo Building
$data_name now . . .
749 .
/launcher rebuild
$data_name
750 echo Building
$app_name now . . .
751 .
/launcher rebuild
$app_name