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' > /etc
/sysctl.d
/30-discourse-swap.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 -F":" '{print $2}'`
280 read_config_result
=`echo $read_config_result | sed "s/^\([\"']\)\(.*\)\1\$/\2/g"`
284 config_line
=`egrep "^ #?$1:" samples/standalone.yml`
285 read_default_result
=`echo $config_line | awk -F":" '{print $2}'`
286 read_default_result
=`echo $read_config_result | sed "s/^\([\"']\)\(.*\)\1\$/\2/g"`
290 ## prompt user for typical Discourse config file values
292 ask_user_for_config
() {
294 # NOTE: Defaults now come from standalone.yml
296 local changelog
=/tmp
/changelog.
$PPID
297 read_config
"DISCOURSE_SMTP_ADDRESS"
298 local smtp_address
=$read_config_result
299 # NOTE: if there are spaces between emails, this breaks, but a human should be paying attention
300 read_config
"DISCOURSE_DEVELOPER_EMAILS"
301 local developer_emails
=$read_config_result
302 read_config
"DISCOURSE_SMTP_PASSWORD"
303 local smtp_password
=$read_config_result
304 read_config
"DISCOURSE_SMTP_PORT"
305 local smtp_port
=$read_config_result
306 read_config
"DISCOURSE_SMTP_USER_NAME"
307 local smtp_user_name
=$read_config_result
308 if [ "$smtp_password" = "pa$$word" ]
312 read_config
"LETSENCRYPT_ACCOUNT_EMAIL"
313 local letsencrypt_account_email
=$read_config_result
314 if [ -z $letsencrypt_account_email ]
316 letsencrypt_account_email
="me@example.com"
318 if [ "$letsencrypt_account_email" = "me@example.com" ]
320 local letsencrypt_status
="ENTER to skip"
322 local letsencrypt_status
="Enter 'OFF' to disable."
325 read_config
"DISCOURSE_HOSTNAME"
326 hostname
=$read_config_result
334 while [[ "$config_ok" == "n" ]]
336 if [ ! -z "$hostname" ]
338 read -p "Hostname for your Discourse? [$hostname]: " new_value
339 if [ ! -z "$new_value" ]
341 hostname
="$new_value"
343 if [[ $hostname =~ ^
[0-9]{1,3}\.
[0-9]{1,3}\.
[0-9]{1,3}\.
[0-9]{1,3}$
]]
346 echo "Discourse requires a DNS hostname. IP addresses are unsupported and will not work."
348 hostname
="discourse.example.com"
352 if [ ! -z "$developer_emails" ]
354 read -p "Email address for admin account(s)? [$developer_emails]: " new_value
355 if [ ! -z "$new_value" ]
357 developer_emails
="$new_value"
361 if [ ! -z "$smtp_address" ]
363 read -p "SMTP server address? [$smtp_address]: " new_value
364 if [ ! -z "$new_value" ]
366 smtp_address
="$new_value"
370 if [ ! -z "$smtp_port" ]
372 read -p "SMTP port? [$smtp_port]: " new_value
373 if [ ! -z "$new_value" ]
375 smtp_port
="$new_value"
380 ## automatically set correct user name based on common mail providers unless it's been set
382 if [ "$smtp_user_name" == "user@example.com" ]
384 if [ "$smtp_address" == "smtp.sparkpostmail.com" ]
386 smtp_user_name
="SMTP_Injection"
388 if [ "$smtp_address" == "smtp.sendgrid.net" ]
390 smtp_user_name
="apikey"
392 if [ "$smtp_address" == "smtp.mailgun.org" ]
394 smtp_user_name
="postmaster@$hostname"
398 if [ ! -z "$smtp_user_name" ]
400 read -p "SMTP user name? [$smtp_user_name]: " new_value
401 if [ ! -z "$new_value" ]
403 smtp_user_name
="$new_value"
407 read -p "SMTP password? [$smtp_password]: " new_value
408 if [ ! -z "$new_value" ]
410 smtp_password
="$new_value"
413 if [ ! -z $letsencrypt_account_email ]
415 read -p "Optional email address for setting up Let's Encrypt? ($letsencrypt_status) [$letsencrypt_account_email]: " new_value
416 if [ ! -z "$new_value" ]
418 letsencrypt_account_email
="$new_value"
419 if [ "${new_value,,}" = "off" ]
421 letsencrypt_status
="ENTER to skip"
423 letsencrypt_status
="Enter 'OFF' to disable."
428 if [ "$letsencrypt_status" == "Enter 'OFF' to disable." ]
430 check_IP_match
$hostname
433 echo -e "\nDoes this look right?\n"
434 echo "Hostname : $hostname"
435 echo "Email : $developer_emails"
436 echo "SMTP address : $smtp_address"
437 echo "SMTP port : $smtp_port"
438 echo "SMTP username : $smtp_user_name"
439 echo "SMTP password : $smtp_password"
441 if [ "$letsencrypt_status" == "Enter 'OFF' to disable." ]
443 echo "Let's Encrypt : $letsencrypt_account_email"
448 read -p "ENTER to continue, 'n' to try again, Ctrl+C to exit: " config_ok
451 sed -i -e "s/^ DISCOURSE_HOSTNAME:.*/ DISCOURSE_HOSTNAME: $hostname/w $changelog" $web_file
456 echo "DISCOURSE_HOSTNAME change failed."
460 sed -i -e "s/^ DISCOURSE_DEVELOPER_EMAILS:.*/ DISCOURSE_DEVELOPER_EMAILS: \'$developer_emails\'/w $changelog" $web_file
465 echo "DISCOURSE_DEVELOPER_EMAILS change failed."
469 sed -i -e "s/^ DISCOURSE_SMTP_ADDRESS:.*/ DISCOURSE_SMTP_ADDRESS: $smtp_address/w $changelog" $web_file
474 echo "DISCOURSE_SMTP_ADDRESS change failed."
478 sed -i -e "s/^ #\?DISCOURSE_SMTP_PORT:.*/ DISCOURSE_SMTP_PORT: $smtp_port/w $changelog" $web_file
483 echo "DISCOURSE_SMTP_PORT change failed."
487 sed -i -e "s/^ #\?DISCOURSE_SMTP_USER_NAME:.*/ DISCOURSE_SMTP_USER_NAME: $smtp_user_name/w $changelog" $web_file
492 echo "DISCOURSE_SMTP_USER_NAME change failed."
496 if [[ "$smtp_password" == *"\""* ]]
499 echo "========================================"
501 echo "Your password contains a quote (\")"
502 echo "Your SMTP Password will not be set. You will need to edit app.yml to enter it."
503 echo "========================================"
507 if [[ "$smtp_password" == *"$SLASH"* ]]
509 if [[ "$smtp_password" == *"$SLASH"* ]]
512 if [[ "$smtp_password" == *"$SLASH"* ]]
515 echo "========================================"
517 echo "Your password contains all available delimiters (+, |, and Q). "
518 echo "Your SMTP Password will not be set. You will need to edit app.yml to enter it."
519 echo "========================================"
525 if [[ "$SLASH" != "BROKEN" ]]
527 sed -i -e "s${SLASH}^ #\?DISCOURSE_SMTP_PASSWORD:.*${SLASH} DISCOURSE_SMTP_PASSWORD: \"${smtp_password}\"${SLASH}w $changelog" $web_file
533 echo "DISCOURSE_SMTP_PASSWORD change failed."
537 if [ "$letsencrypt_status" = "ENTER to skip" ]
539 local src
='^ #\?- "templates\/web.ssl.template.yml"'
540 local dst
=' #\- "templates\/web.ssl.template.yml"'
541 sed -i -e "s/$src/$dst/w $changelog" $web_file
542 if [ ! -s $changelog ]
545 echo "web.ssl.template.yml NOT DISABLED--Are you using a non-standard template?"
547 local src
='^ #\?- "templates\/web.letsencrypt.ssl.template.yml"'
548 local dst
=' #- "templates\/web.letsencrypt.ssl.template.yml"'
550 sed -i -e "s/$src/$dst/w $changelog" $web_file
551 if [ ! -s $changelog ]
554 echo "web.ssl.template.yml NOT DISABLED--Are you using a non-standard template?"
556 else # enable let's encrypt
557 echo "Let's Encrypt will be enabled for $letsencrypt_account_email"
558 sed -i -e "s/^ #\?LETSENCRYPT_ACCOUNT_EMAIL:.*/ LETSENCRYPT_ACCOUNT_EMAIL: $letsencrypt_account_email/w $changelog" $web_file
563 echo "LETSENCRYPT_ACCOUNT_EMAIL change failed."
566 local src
='^ #\?- "templates\/web.ssl.template.yml"'
567 local dst
=' \- "templates\/web.ssl.template.yml"'
568 sed -i -e "s/$src/$dst/w $changelog" $web_file
571 echo "web.ssl.template.yml enabled"
574 echo "web.ssl.template.yml NOT ENABLED--was it on already?"
576 local src
='^ #\?- "templates\/web.letsencrypt.ssl.template.yml"'
577 local dst
=' - "templates\/web.letsencrypt.ssl.template.yml"'
579 sed -i -e "s/$src/$dst/w $changelog" $web_file
582 echo "letsencrypt.ssl.template.yml enabled"
585 echo "letsencrypt.ssl.template.yml NOT ENABLED -- was it on already?"
589 if [ "$update_ok" == "y" ]
591 echo -e "\nConfiguration file at $config_file updated successfully!\n"
593 echo -e "\nUnfortunately, there was an error changing $config_file\n"
594 echo -d "This may happen if you have made unexpected changes."
600 ## is our config file valid? Does it have the required fields set?
606 for x
in DISCOURSE_SMTP_ADDRESS DISCOURSE_SMTP_USER_NAME DISCOURSE_SMTP_PASSWORD \
607 DISCOURSE_DEVELOPER_EMAILS DISCOURSE_HOSTNAME
610 local result
=$read_config_result
612 local default
=$read_default_result
614 if [ ! -z "$result" ]
616 if [[ "$config_line" = *"$default"* ]]
618 echo "$x left at incorrect default of $default"
621 config_val
=`echo $config_line | awk '{print $2}'`
622 if [ -z $config_val ]
624 echo "$x was not configured"
628 echo "$x not present"
633 if [ "$valid_config" != "y" ]; then
634 echo -e "\nSorry, these $web_file settings aren't valid -- can't continue!"
635 echo "If you have unusual requirements, edit $web_file and then: "
636 echo "./launcher bootstrap $app_name"
643 ## template file names
646 if [ "$1" == "2container" ]
650 web_template
=samples
/web_only.yml
651 data_template
=samples
/data.yml
652 web_file
=containers
/$app_name.yml
653 data_file
=containers
/$data_name.yml
657 web_template
=samples
/standalone.yml
659 web_file
=containers
/$app_name.yml
660 data_file
=containers
/$app_name.yml
662 changelog
=/tmp
/changelog
665 ## Check requirements before creating a copy of a config file we won't edit
668 check_and_install_docker
669 check_disk_and_memory
671 if [ -a "$web_file" ]
673 echo "The configuration file $web_file already exists!"
675 echo ". . . reconfiguring . . ."
678 DATE
=`date +"%Y-%m-%d-%H%M%S"`
679 BACKUP
=$app_name.yml.
$DATE.bak
680 echo Saving old
file as
$BACKUP
681 cp $web_file containers
/$BACKUP
682 echo "Stopping existing container in 5 seconds or Control-C to cancel."
688 cp -v $web_template $web_file
689 if [ "$data_name" == "data" ]
691 echo "--------------------------------------------------"
692 echo "This two container setup is currently unsupported. Use at your own risk!"
693 echo "--------------------------------------------------"
694 DISCOURSE_DB_PASSWORD
=`date +%s | sha256sum | base64 | head -c 20`
696 sed -i -e "s/DISCOURSE_DB_PASSWORD: SOME_SECRET/DISCOURSE_DB_PASSWORD: $DISCOURSE_DB_PASSWORD/w $changelog" $web_file
701 echo "Problem changing DISCOURSE_DB_PASSWORD" in $web_file
704 cp -v $data_template $data_file
706 sed -i -e "s/password ${quote}SOME_SECRET${quote}/password '$DISCOURSE_DB_PASSWORD'/w $changelog" $data_file
711 echo "Problem changing DISCOURSE_DB_PASSWORD" in $data_file
721 ## if we reach this point without exiting, OK to proceed
722 ## rebuild won't fail if there's nothing to rebuild and does the restart
724 echo "Updates successful. Rebuilding in 5 seconds."
725 sleep 5 # Just a chance to ^C in case they were too fast on the draw
726 if [ "$data_name" == "$app_name" ]
728 echo Building
$app_name
729 .
/launcher rebuild
$app_name
731 echo Building
$data_name now . . .
732 .
/launcher rebuild
$data_name
733 echo Building
$app_name now . . .
734 .
/launcher rebuild
$app_name