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 ## some VMs report just under 1GB of RAM, so
118 ## make an exception and allow those with more
120 mem
=`free -m --si | awk ' /Mem:/ {print $2}'`
121 if [ "$mem" -ge 990 -a "$mem" -lt 1000 ]; then
124 echo `free -g --si | awk ' /Mem:/ {print $2} '`
129 ## Do we have enough memory and disk space for Discourse?
131 check_disk_and_memory
() {
135 if [ "$os_type" == "Darwin" ]; then
136 avail_mem
=$
(check_osx_memory
)
138 avail_mem
=$
(check_linux_memory
)
141 if [ "$avail_mem" -lt 1 ]; then
142 echo "WARNING: Discourse requires 1GB RAM to run. This system does not appear"
143 echo "to have sufficient memory."
145 echo "Your site may not work properly, or future upgrades of Discourse may not"
146 echo "complete successfully."
150 if [ "$avail_mem" -le 2 ]; then
151 total_swap
=`free -g --si | awk ' /Swap:/ {print $2} '`
153 if [ "$total_swap" -lt 2 ]; then
154 echo "WARNING: Discourse requires at least 2GB of swap when running with 2GB of RAM"
155 echo "or less. This system does not appear to have sufficient swap space."
157 echo "Without sufficient swap space, your site may not work properly, and future"
158 echo "upgrades of Discourse may not complete successfully."
160 echo "Ctrl+C to exit or wait 5 seconds to have a 2GB swapfile created."
164 ## derived from https://meta.discourse.org/t/13880
166 install -o root
-g root
-m 0600 /dev
/null
/swapfile
167 fallocate
-l 2G
/swapfile
170 echo "/swapfile swap swap auto 0 0" |
tee -a /etc
/fstab
171 sysctl
-w vm.swappiness
=10
172 echo 'vm.swappiness = 10' > /etc
/sysctl.d
/30-discourse-swap.conf
174 total_swap
=`free -g --si | awk ' /Swap:/ {print $2} '`
175 if [ "$total_swap" -lt 2 ]; then
176 echo "Failed to create swap: are you root? Are you running on real hardware, or a fully virtualized server?"
184 free_disk
="$(df /var | tail -n 1 | awk '{print $4}')"
185 if [ "$free_disk" -lt 5000 ]; then
186 echo "WARNING: Discourse requires at least 5GB free disk space. This system"
187 echo "does not appear to have sufficient disk space."
189 echo "Insufficient disk space may result in problems running your site, and"
190 echo "may not even allow Discourse installation to complete successfully."
192 echo "Please free up some space, or expand your disk, before continuing."
194 echo "Run \`apt-get autoremove && apt-get autoclean\` to clean up unused"
195 echo "packages and \`./launcher cleanup\` to remove stale Docker containers."
203 ## If we have lots of RAM or lots of CPUs, bump up the defaults to scale better
205 scale_ram_and_cpu
() {
207 local changelog
=/tmp
/changelog.
$PPID
208 # grab info about total system ram and physical (NOT LOGICAL!) CPU cores
212 if [ "$os_type" == "Darwin" ]; then
213 avail_gb
=$
(check_osx_memory
)
214 avail_cores
=`sysctl hw.ncpu | awk '/hw.ncpu:/ {print $2}'`
216 avail_gb
=$
(check_linux_memory
)
217 avail_cores
=$
((`awk '/cpu cores/ {print $4;exit}' /proc/cpuinfo`*`sort /proc/cpuinfo | uniq | grep -c "physical id"`))
219 echo "Found ${avail_gb}GB of memory and $avail_cores physical CPU cores"
221 # db_shared_buffers: 128MB for 1GB, 256MB for 2GB, or 256MB * GB, max 4096MB
222 if [ "$avail_gb" -eq "1" ]
224 db_shared_buffers
=128
226 if [ "$avail_gb" -eq "2" ]
228 db_shared_buffers
=256
230 db_shared_buffers
=$
(( 256 * $avail_gb ))
233 db_shared_buffers
=$
(( db_shared_buffers
< 4096 ? db_shared_buffers
: 4096 ))
235 sed -i -e "s/^ #\?db_shared_buffers:.*/ db_shared_buffers: \"${db_shared_buffers}MB\"/w $changelog" $data_file
238 echo "setting db_shared_buffers = ${db_shared_buffers}MB"
242 # UNICORN_WORKERS: 2 * GB for 2GB or less, or 2 * CPU, max 8
243 if [ "$avail_gb" -le "2" ]
245 unicorn_workers
=$
(( 2 * $avail_gb ))
247 unicorn_workers
=$
(( 2 * $avail_cores ))
249 unicorn_workers
=$
(( unicorn_workers
< 8 ? unicorn_workers
: 8 ))
251 sed -i -e "s/^ #\?UNICORN_WORKERS:.*/ UNICORN_WORKERS: ${unicorn_workers}/w $changelog" $web_file
254 echo "setting UNICORN_WORKERS = ${unicorn_workers}"
258 echo $data_file memory parameters updated.
263 ## standard http / https ports must not be occupied
268 echo "Ports 80 and 443 are free for use"
273 ## check a port to see if it is already in use
277 local valid
=$
(netstat
-tln |
awk '{print $4}' |
grep ":${1}\$")
279 if [ -n "$valid" ]; then
280 echo "Port ${1} appears to already be in use."
282 echo "This will show you what command is using port ${1}"
283 lsof
-i tcp
:${1} -s tcp
:listen
285 echo "If you are trying to run Discourse simultaneously with another web"
286 echo "server like Apache or nginx, you will need to bind to a different port"
288 echo "See https://meta.discourse.org/t/17247"
290 echo "If you are reconfiguring an already-configured Discourse, use "
292 echo "./launcher stop app"
294 echo "to stop Discourse before you reconfigure it and try again."
300 ## read a variable from the config file
303 config_line
=`egrep "^ #?$1:" $web_file`
304 read_config_result
=`echo $config_line | awk -F":" '{print $2}'`
305 read_config_result
=`echo $read_config_result | sed "s/^\([\"']\)\(.*\)\1\$/\2/g"`
309 config_line
=`egrep "^ #?$1:" samples/standalone.yml`
310 read_default_result
=`echo $config_line | awk -F":" '{print $2}'`
311 read_default_result
=`echo $read_config_result | sed "s/^\([\"']\)\(.*\)\1\$/\2/g"`
315 ## prompt user for typical Discourse config file values
317 ask_user_for_config
() {
319 # NOTE: Defaults now come from standalone.yml
321 local changelog
=/tmp
/changelog.
$PPID
322 read_config
"DISCOURSE_SMTP_ADDRESS"
323 local smtp_address
=$read_config_result
324 # NOTE: if there are spaces between emails, this breaks, but a human should be paying attention
325 read_config
"DISCOURSE_DEVELOPER_EMAILS"
326 local developer_emails
=$read_config_result
327 read_config
"DISCOURSE_SMTP_PASSWORD"
328 local smtp_password
=$read_config_result
329 read_config
"DISCOURSE_SMTP_PORT"
330 local smtp_port
=$read_config_result
331 read_config
"DISCOURSE_SMTP_USER_NAME"
332 local smtp_user_name
=$read_config_result
333 if [ "$smtp_password" = "pa$$word" ]
337 read_config
"LETSENCRYPT_ACCOUNT_EMAIL"
338 local letsencrypt_account_email
=$read_config_result
339 if [ -z $letsencrypt_account_email ]
341 letsencrypt_account_email
="me@example.com"
343 if [ "$letsencrypt_account_email" = "me@example.com" ]
345 local letsencrypt_status
="ENTER to skip"
347 local letsencrypt_status
="Enter 'OFF' to disable."
350 read_config
"DISCOURSE_HOSTNAME"
351 hostname
=$read_config_result
359 while [[ "$config_ok" == "n" ]]
361 if [ ! -z "$hostname" ]
363 read -p "Hostname for your Discourse? [$hostname]: " new_value
364 if [ ! -z "$new_value" ]
366 hostname
="$new_value"
368 if [[ $hostname =~ ^
[0-9]{1,3}\.
[0-9]{1,3}\.
[0-9]{1,3}\.
[0-9]{1,3}$
]]
371 echo "Discourse requires a DNS hostname. IP addresses are unsupported and will not work."
373 hostname
="discourse.example.com"
377 if [ ! -z "$developer_emails" ]
379 read -p "Email address for admin account(s)? [$developer_emails]: " new_value
380 if [ ! -z "$new_value" ]
382 developer_emails
="$new_value"
386 if [ ! -z "$smtp_address" ]
388 read -p "SMTP server address? [$smtp_address]: " new_value
389 if [ ! -z "$new_value" ]
391 smtp_address
="$new_value"
395 if [ ! -z "$smtp_port" ]
397 read -p "SMTP port? [$smtp_port]: " new_value
398 if [ ! -z "$new_value" ]
400 smtp_port
="$new_value"
405 ## automatically set correct user name based on common mail providers unless it's been set
407 if [ "$smtp_user_name" == "user@example.com" ]
409 if [ "$smtp_address" == "smtp.sparkpostmail.com" ]
411 smtp_user_name
="SMTP_Injection"
413 if [ "$smtp_address" == "smtp.sendgrid.net" ]
415 smtp_user_name
="apikey"
417 if [ "$smtp_address" == "smtp.mailgun.org" ]
419 smtp_user_name
="postmaster@$hostname"
423 if [ ! -z "$smtp_user_name" ]
425 read -p "SMTP user name? [$smtp_user_name]: " new_value
426 if [ ! -z "$new_value" ]
428 smtp_user_name
="$new_value"
432 read -p "SMTP password? [$smtp_password]: " new_value
433 if [ ! -z "$new_value" ]
435 smtp_password
="$new_value"
438 if [ ! -z $letsencrypt_account_email ]
440 read -p "Optional email address for setting up Let's Encrypt? ($letsencrypt_status) [$letsencrypt_account_email]: " new_value
441 if [ ! -z "$new_value" ]
443 letsencrypt_account_email
="$new_value"
444 if [ "${new_value,,}" = "off" ]
446 letsencrypt_status
="ENTER to skip"
448 letsencrypt_status
="Enter 'OFF' to disable."
453 if [ "$letsencrypt_status" == "Enter 'OFF' to disable." ]
455 check_IP_match
$hostname
458 echo -e "\nDoes this look right?\n"
459 echo "Hostname : $hostname"
460 echo "Email : $developer_emails"
461 echo "SMTP address : $smtp_address"
462 echo "SMTP port : $smtp_port"
463 echo "SMTP username : $smtp_user_name"
464 echo "SMTP password : $smtp_password"
466 if [ "$letsencrypt_status" == "Enter 'OFF' to disable." ]
468 echo "Let's Encrypt : $letsencrypt_account_email"
473 read -p "ENTER to continue, 'n' to try again, Ctrl+C to exit: " config_ok
476 sed -i -e "s/^ DISCOURSE_HOSTNAME:.*/ DISCOURSE_HOSTNAME: $hostname/w $changelog" $web_file
481 echo "DISCOURSE_HOSTNAME change failed."
485 sed -i -e "s/^ DISCOURSE_DEVELOPER_EMAILS:.*/ DISCOURSE_DEVELOPER_EMAILS: \'$developer_emails\'/w $changelog" $web_file
490 echo "DISCOURSE_DEVELOPER_EMAILS change failed."
494 sed -i -e "s/^ DISCOURSE_SMTP_ADDRESS:.*/ DISCOURSE_SMTP_ADDRESS: $smtp_address/w $changelog" $web_file
499 echo "DISCOURSE_SMTP_ADDRESS change failed."
503 sed -i -e "s/^ #\?DISCOURSE_SMTP_PORT:.*/ DISCOURSE_SMTP_PORT: $smtp_port/w $changelog" $web_file
508 echo "DISCOURSE_SMTP_PORT change failed."
512 sed -i -e "s/^ #\?DISCOURSE_SMTP_USER_NAME:.*/ DISCOURSE_SMTP_USER_NAME: $smtp_user_name/w $changelog" $web_file
517 echo "DISCOURSE_SMTP_USER_NAME change failed."
521 if [[ "$smtp_password" == *"\""* ]]
524 echo "========================================"
526 echo "Your password contains a quote (\")"
527 echo "Your SMTP Password will not be set. You will need to edit app.yml to enter it."
528 echo "========================================"
532 if [[ "$smtp_password" == *"$SLASH"* ]]
534 if [[ "$smtp_password" == *"$SLASH"* ]]
537 if [[ "$smtp_password" == *"$SLASH"* ]]
540 echo "========================================"
542 echo "Your password contains all available delimiters (+, |, and Q). "
543 echo "Your SMTP Password will not be set. You will need to edit app.yml to enter it."
544 echo "========================================"
550 if [[ "$SLASH" != "BROKEN" ]]
552 sed -i -e "s${SLASH}^ #\?DISCOURSE_SMTP_PASSWORD:.*${SLASH} DISCOURSE_SMTP_PASSWORD: \"${smtp_password}\"${SLASH}w $changelog" $web_file
558 echo "DISCOURSE_SMTP_PASSWORD change failed."
562 if [ "$letsencrypt_status" = "ENTER to skip" ]
564 local src
='^ #\?- "templates\/web.ssl.template.yml"'
565 local dst
=' #\- "templates\/web.ssl.template.yml"'
566 sed -i -e "s/$src/$dst/w $changelog" $web_file
567 if [ ! -s $changelog ]
570 echo "web.ssl.template.yml NOT DISABLED--Are you using a non-standard template?"
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
576 if [ ! -s $changelog ]
579 echo "web.ssl.template.yml NOT DISABLED--Are you using a non-standard template?"
581 else # enable let's encrypt
582 echo "Let's Encrypt will be enabled for $letsencrypt_account_email"
583 sed -i -e "s/^ #\?LETSENCRYPT_ACCOUNT_EMAIL:.*/ LETSENCRYPT_ACCOUNT_EMAIL: $letsencrypt_account_email/w $changelog" $web_file
588 echo "LETSENCRYPT_ACCOUNT_EMAIL change failed."
591 local src
='^ #\?- "templates\/web.ssl.template.yml"'
592 local dst
=' \- "templates\/web.ssl.template.yml"'
593 sed -i -e "s/$src/$dst/w $changelog" $web_file
596 echo "web.ssl.template.yml enabled"
599 echo "web.ssl.template.yml NOT ENABLED--was it on already?"
601 local src
='^ #\?- "templates\/web.letsencrypt.ssl.template.yml"'
602 local dst
=' - "templates\/web.letsencrypt.ssl.template.yml"'
604 sed -i -e "s/$src/$dst/w $changelog" $web_file
607 echo "letsencrypt.ssl.template.yml enabled"
610 echo "letsencrypt.ssl.template.yml NOT ENABLED -- was it on already?"
614 if [ "$update_ok" == "y" ]
616 echo -e "\nConfiguration file at $config_file updated successfully!\n"
618 echo -e "\nUnfortunately, there was an error changing $config_file\n"
619 echo -d "This may happen if you have made unexpected changes."
625 ## is our config file valid? Does it have the required fields set?
631 for x
in DISCOURSE_SMTP_ADDRESS DISCOURSE_SMTP_USER_NAME DISCOURSE_SMTP_PASSWORD \
632 DISCOURSE_DEVELOPER_EMAILS DISCOURSE_HOSTNAME
635 local result
=$read_config_result
637 local default
=$read_default_result
639 if [ ! -z "$result" ]
641 if [[ "$config_line" = *"$default"* ]]
643 echo "$x left at incorrect default of $default"
646 config_val
=`echo $config_line | awk '{print $2}'`
647 if [ -z $config_val ]
649 echo "$x was not configured"
653 echo "$x not present"
658 if [ "$valid_config" != "y" ]; then
659 echo -e "\nSorry, these $web_file settings aren't valid -- can't continue!"
660 echo "If you have unusual requirements, edit $web_file and then: "
661 echo "./launcher bootstrap $app_name"
668 ## template file names
671 if [ "$1" == "2container" ]
675 web_template
=samples
/web_only.yml
676 data_template
=samples
/data.yml
677 web_file
=containers
/$app_name.yml
678 data_file
=containers
/$data_name.yml
682 web_template
=samples
/standalone.yml
684 web_file
=containers
/$app_name.yml
685 data_file
=containers
/$app_name.yml
687 changelog
=/tmp
/changelog
690 ## Check requirements before creating a copy of a config file we won't edit
693 check_and_install_docker
694 check_disk_and_memory
696 if [ -a "$web_file" ]
698 echo "The configuration file $web_file already exists!"
700 echo ". . . reconfiguring . . ."
703 DATE
=`date +"%Y-%m-%d-%H%M%S"`
704 BACKUP
=$app_name.yml.
$DATE.bak
705 echo Saving old
file as
$BACKUP
706 cp $web_file containers
/$BACKUP
707 echo "Stopping existing container in 5 seconds or Control-C to cancel."
713 cp -v $web_template $web_file
714 if [ "$data_name" == "data" ]
716 echo "--------------------------------------------------"
717 echo "This two container setup is currently unsupported. Use at your own risk!"
718 echo "--------------------------------------------------"
719 DISCOURSE_DB_PASSWORD
=`date +%s | sha256sum | base64 | head -c 20`
721 sed -i -e "s/DISCOURSE_DB_PASSWORD: SOME_SECRET/DISCOURSE_DB_PASSWORD: $DISCOURSE_DB_PASSWORD/w $changelog" $web_file
726 echo "Problem changing DISCOURSE_DB_PASSWORD" in $web_file
729 cp -v $data_template $data_file
731 sed -i -e "s/password ${quote}SOME_SECRET${quote}/password '$DISCOURSE_DB_PASSWORD'/w $changelog" $data_file
736 echo "Problem changing DISCOURSE_DB_PASSWORD" in $data_file
746 ## if we reach this point without exiting, OK to proceed
747 ## rebuild won't fail if there's nothing to rebuild and does the restart
749 echo "Updates successful. Rebuilding in 5 seconds."
750 sleep 5 # Just a chance to ^C in case they were too fast on the draw
751 if [ "$data_name" == "$app_name" ]
753 echo Building
$app_name
754 .
/launcher rebuild
$app_name
756 echo Building
$data_name now . . .
757 .
/launcher rebuild
$data_name
758 echo Building
$app_name now . . .
759 .
/launcher rebuild
$app_name