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: Port 443 of computer does not appear to be accessible using hostname: $HOST."
56 if connect_to_port
$HOST 80; then
58 echo SUCCESS
: A connection to port
80 succeeds
!
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 "WARNING: Connection to http://$HOST (port 80) also fails."
65 echo "This suggests that $HOST resolves to some IP address that does not reach this "
66 echo machine where you are installing discourse.
69 echo "The first thing to do is confirm that $HOST resolves to the IP address of this server."
70 echo You usually
do this
at the same place you purchased the domain.
72 echo If you are sure that the IP address resolves correctly
, it could be a firewall issue.
73 echo A web search
for \"open ports YOUR CLOUD SERVICE
\" might
help.
75 echo This tool is designed only
for the most standard installations. If you cannot resolve
76 echo the issue above
, you will need to edit containers
/app.yml yourself and
then type
78 echo .
/launcher rebuild app
83 echo "Continuing without port check."
91 check_and_install_docker
() {
92 docker_path
=`which docker.io || which docker`
93 if [ -z $docker_path ]; then
94 read -p "Docker not installed. Enter to install from https://get.docker.com/ or Ctrl+C to exit"
95 curl https
://get.docker.com
/ | sh
97 docker_path
=`which docker.io || which docker`
98 if [ -z $docker_path ]; then
99 echo Docker
install failed. Quitting.
105 ## What are we running on
112 ## OS X available memory
115 echo `free -m | awk '/Mem:/ {print $2}'`
119 ## Linux available memory
121 check_linux_memory
() {
122 ## some VMs report just under 1GB of RAM, so
123 ## make an exception and allow those with more
125 mem
=`free -m --si | awk ' /Mem:/ {print $2}'`
126 if [ "$mem" -ge 990 -a "$mem" -lt 1000 ]; then
129 echo `free -g --si | awk ' /Mem:/ {print $2} '`
134 ## Do we have enough memory and disk space for Discourse?
136 check_disk_and_memory
() {
140 if [ "$os_type" == "Darwin" ]; then
141 avail_mem
=$
(check_osx_memory
)
143 avail_mem
=$
(check_linux_memory
)
146 if [ "$avail_mem" -lt 1 ]; then
147 echo "WARNING: Discourse requires 1GB RAM to run. This system does not appear"
148 echo "to have sufficient memory."
150 echo "Your site may not work properly, or future upgrades of Discourse may not"
151 echo "complete successfully."
155 if [ "$avail_mem" -le 2 ]; then
156 total_swap
=`free -g --si | awk ' /Swap:/ {print $2} '`
158 if [ "$total_swap" -lt 2 ]; then
159 echo "WARNING: Discourse requires at least 2GB of swap when running with 2GB of RAM"
160 echo "or less. This system does not appear to have sufficient swap space."
162 echo "Without sufficient swap space, your site may not work properly, and future"
163 echo "upgrades of Discourse may not complete successfully."
165 echo "Ctrl+C to exit or wait 5 seconds to have a 2GB swapfile created."
169 ## derived from https://meta.discourse.org/t/13880
171 install -o root
-g root
-m 0600 /dev
/null
/swapfile
172 fallocate
-l 2G
/swapfile
175 echo "/swapfile swap swap auto 0 0" |
tee -a /etc
/fstab
176 sysctl
-w vm.swappiness
=10
177 echo 'vm.swappiness = 10' > /etc
/sysctl.d
/30-discourse-swap.conf
179 total_swap
=`free -g --si | awk ' /Swap:/ {print $2} '`
180 if [ "$total_swap" -lt 2 ]; then
181 echo "Failed to create swap: are you root? Are you running on real hardware, or a fully virtualized server?"
189 free_disk
="$(df /var | tail -n 1 | awk '{print $4}')"
190 if [ "$free_disk" -lt 5000 ]; then
191 echo "WARNING: Discourse requires at least 5GB free disk space. This system"
192 echo "does not appear to have sufficient disk space."
194 echo "Insufficient disk space may result in problems running your site, and"
195 echo "may not even allow Discourse installation to complete successfully."
197 echo "Please free up some space, or expand your disk, before continuing."
199 echo "Run \`apt-get autoremove && apt-get autoclean\` to clean up unused"
200 echo "packages and \`./launcher cleanup\` to remove stale Docker containers."
208 ## If we have lots of RAM or lots of CPUs, bump up the defaults to scale better
210 scale_ram_and_cpu
() {
212 local changelog
=/tmp
/changelog.
$PPID
213 # grab info about total system ram and physical (NOT LOGICAL!) CPU cores
217 if [ "$os_type" == "Darwin" ]; then
218 avail_gb
=$
(check_osx_memory
)
219 avail_cores
=`sysctl hw.ncpu | awk '/hw.ncpu:/ {print $2}'`
221 avail_gb
=$
(check_linux_memory
)
222 avail_cores
=$
((`awk '/cpu cores/ {print $4;exit}' /proc/cpuinfo`*`sort /proc/cpuinfo | uniq | grep -c "physical id"`))
224 echo "Found ${avail_gb}GB of memory and $avail_cores physical CPU cores"
226 # db_shared_buffers: 128MB for 1GB, 256MB for 2GB, or 256MB * GB, max 4096MB
227 if [ "$avail_gb" -eq "1" ]
229 db_shared_buffers
=128
231 if [ "$avail_gb" -eq "2" ]
233 db_shared_buffers
=256
235 db_shared_buffers
=$
(( 256 * $avail_gb ))
238 db_shared_buffers
=$
(( db_shared_buffers
< 4096 ? db_shared_buffers
: 4096 ))
240 sed -i -e "s/^ #\?db_shared_buffers:.*/ db_shared_buffers: \"${db_shared_buffers}MB\"/w $changelog" $data_file
243 echo "setting db_shared_buffers = ${db_shared_buffers}MB"
247 # UNICORN_WORKERS: 2 * GB for 2GB or less, or 2 * CPU, max 8
248 if [ "$avail_gb" -le "2" ]
250 unicorn_workers
=$
(( 2 * $avail_gb ))
252 unicorn_workers
=$
(( 2 * $avail_cores ))
254 unicorn_workers
=$
(( unicorn_workers
< 8 ? unicorn_workers
: 8 ))
256 sed -i -e "s/^ #\?UNICORN_WORKERS:.*/ UNICORN_WORKERS: ${unicorn_workers}/w $changelog" $web_file
259 echo "setting UNICORN_WORKERS = ${unicorn_workers}"
263 echo $data_file memory parameters updated.
268 ## standard http / https ports must not be occupied
273 echo "Ports 80 and 443 are free for use"
278 ## check a port to see if it is already in use
282 local valid
=$
(netstat
-tln |
awk '{print $4}' |
grep ":${1}\$")
284 if [ -n "$valid" ]; then
285 echo "Port ${1} appears to already be in use."
287 echo "This will show you what command is using port ${1}"
288 lsof
-i tcp
:${1} -s tcp
:listen
290 echo "If you are trying to run Discourse simultaneously with another web"
291 echo "server like Apache or nginx, you will need to bind to a different port"
293 echo "See https://meta.discourse.org/t/17247"
295 echo "If you are reconfiguring an already-configured Discourse, use "
297 echo "./launcher stop app"
299 echo "to stop Discourse before you reconfigure it and try again."
305 ## read a variable from the config file
308 config_line
=`egrep "^ #?$1:" $web_file`
309 read_config_result
=`echo $config_line | awk -F":" '{print $2}'`
310 read_config_result
=`echo $read_config_result | sed "s/^\([\"']\)\(.*\)\1\$/\2/g"`
314 config_line
=`egrep "^ #?$1:" samples/standalone.yml`
315 read_default_result
=`echo $config_line | awk -F":" '{print $2}'`
316 read_default_result
=`echo $read_config_result | sed "s/^\([\"']\)\(.*\)\1\$/\2/g"`
319 assert_maxmind_license_key
() {
320 echo "Checking if $web_file has MAXMIND placeholder."
321 if ! grep DISCOURSE_MAXMIND_LICENSE_KEY
$web_file
323 echo "Adding MAXMIND placeholder to $web_file"
324 sed -i -e 's/LETSENCRYPT_ACCOUNT_EMAIL/a\ \ #DISCOURSE_MAXMIND_LICENSE_KEY: 1234567890123456' $web_file
329 ## prompt user for typical Discourse config file values
331 ask_user_for_config
() {
333 # NOTE: Defaults now come from standalone.yml
335 local changelog
=/tmp
/changelog.
$PPID
336 read_config
"DISCOURSE_SMTP_ADDRESS"
337 local smtp_address
=$read_config_result
338 # NOTE: if there are spaces between emails, this breaks, but a human should be paying attention
339 read_config
"DISCOURSE_DEVELOPER_EMAILS"
340 local developer_emails
=$read_config_result
341 read_config
"DISCOURSE_SMTP_PASSWORD"
342 local smtp_password
=$read_config_result
343 read_config
"DISCOURSE_SMTP_PORT"
344 local smtp_port
=$read_config_result
345 read_config
"DISCOURSE_SMTP_USER_NAME"
346 local smtp_user_name
=$read_config_result
347 if [ "$smtp_password" = "pa$$word" ]
351 read_config
"LETSENCRYPT_ACCOUNT_EMAIL"
352 local letsencrypt_account_email
=$read_config_result
353 if [ -z $letsencrypt_account_email ]
355 letsencrypt_account_email
="me@example.com"
357 if [ "$letsencrypt_account_email" = "me@example.com" ]
359 local letsencrypt_status
="ENTER to skip"
361 local letsencrypt_status
="Enter 'OFF' to disable."
364 read_config
"DISCOURSE_MAXMIND_LICENSE_KEY"
365 local maxmind_license_key
=$read_config_result
366 if [ -z $maxmind_license_key ]
368 maxmind_license_key
="1234567890123456"
370 if [ "$maxmind_license_key" == "1234567890123456" ]
372 local maxmind_status
="ENTER to continue without MAXMIND GeoLite2 geolocation database"
375 read_config
"DISCOURSE_HOSTNAME"
376 hostname
=$read_config_result
384 while [[ "$config_ok" == "n" ]]
386 if [ ! -z "$hostname" ]
388 read -p "Hostname for your Discourse? [$hostname]: " new_value
389 if [ ! -z "$new_value" ]
391 hostname
="$new_value"
393 if [[ $hostname =~ ^
[0-9]{1,3}\.
[0-9]{1,3}\.
[0-9]{1,3}\.
[0-9]{1,3}$
]]
396 echo "Discourse requires a DNS hostname. IP addresses are unsupported and will not work."
398 hostname
="discourse.example.com"
402 check_IP_match
$hostname
404 if [ ! -z "$developer_emails" ]
406 local email_valid
="n"
407 until [ "$email_valid" == "y" ]
409 read -p "Email address for admin account(s)? [$developer_emails]: " new_value
410 if [ ! -z "$new_value" ]
412 if [[ ${#new_value} -ge 7 && $new_value == *@
* ]]
414 developer_emails
="$new_value"
418 echo "[Error] Invalid email address"
427 if [ ! -z "$smtp_address" ]
429 read -p "SMTP server address? [$smtp_address]: " new_value
430 if [ ! -z "$new_value" ]
432 smtp_address
="$new_value"
436 if [ ! -z "$smtp_port" ]
438 read -p "SMTP port? [$smtp_port]: " new_value
439 if [ ! -z "$new_value" ]
441 smtp_port
="$new_value"
446 ## automatically set correct user name based on common mail providers unless it's been set
448 if [ "$smtp_user_name" == "user@example.com" ]
450 if [ "$smtp_address" == "smtp.sparkpostmail.com" ]
452 smtp_user_name
="SMTP_Injection"
454 if [ "$smtp_address" == "smtp.sendgrid.net" ]
456 smtp_user_name
="apikey"
458 if [ "$smtp_address" == "smtp.mailgun.org" ]
460 smtp_user_name
="postmaster@$hostname"
464 if [ ! -z "$smtp_user_name" ]
466 read -p "SMTP user name? [$smtp_user_name]: " new_value
467 if [ ! -z "$new_value" ]
469 smtp_user_name
="$new_value"
473 read -p "SMTP password? [$smtp_password]: " new_value
474 if [ ! -z "$new_value" ]
476 smtp_password
="$new_value"
479 if [ ! -z $letsencrypt_account_email ]
481 read -p "Optional email address for Let's Encrypt warnings? ($letsencrypt_status) [$letsencrypt_account_email]: " new_value
482 if [ ! -z "$new_value" ]
484 letsencrypt_account_email
="$new_value"
485 if [ "${new_value,,}" = "off" ]
487 letsencrypt_status
="ENTER to skip"
489 letsencrypt_status
="Enter 'OFF' to disable."
494 read_config
"DISCOURSE_MAXMIND_LICENSE_KEY"
495 local maxmind_license_key
=$read_config_result
496 read -p "Optional Maxmind License key ($maxmind_status) [$maxmind_license_key]: " new_value
497 if [ ! -z "$new_value" ]
499 maxmind_license_key
="$new_value"
502 echo -e "\nDoes this look right?\n"
503 echo "Hostname : $hostname"
504 echo "Email : $developer_emails"
505 echo "SMTP address : $smtp_address"
506 echo "SMTP port : $smtp_port"
507 echo "SMTP username : $smtp_user_name"
508 echo "SMTP password : $smtp_password"
510 if [ "$letsencrypt_status" == "Enter 'OFF' to disable." ]
512 echo "Let's Encrypt : $letsencrypt_account_email"
515 if [ "$maxmind_license_key" != "1234567890123456" ]
517 echo "Maxmind license: $maxmind_license_key"
519 echo "Maxmind license: (unset)"
523 read -p "ENTER to continue, 'n' to try again, Ctrl+C to exit: " config_ok
526 sed -i -e "s/^ DISCOURSE_HOSTNAME:.*/ DISCOURSE_HOSTNAME: $hostname/w $changelog" $web_file
531 echo "DISCOURSE_HOSTNAME change failed."
535 sed -i -e "s/^ DISCOURSE_DEVELOPER_EMAILS:.*/ DISCOURSE_DEVELOPER_EMAILS: \'$developer_emails\'/w $changelog" $web_file
540 echo "DISCOURSE_DEVELOPER_EMAILS change failed."
544 sed -i -e "s/^ DISCOURSE_SMTP_ADDRESS:.*/ DISCOURSE_SMTP_ADDRESS: $smtp_address/w $changelog" $web_file
549 echo "DISCOURSE_SMTP_ADDRESS change failed."
553 sed -i -e "s/^ #\?DISCOURSE_SMTP_PORT:.*/ DISCOURSE_SMTP_PORT: $smtp_port/w $changelog" $web_file
558 echo "DISCOURSE_SMTP_PORT change failed."
562 sed -i -e "s/^ #\?DISCOURSE_SMTP_USER_NAME:.*/ DISCOURSE_SMTP_USER_NAME: $smtp_user_name/w $changelog" $web_file
567 echo "DISCOURSE_SMTP_USER_NAME change failed."
571 if [[ "$smtp_password" == *"\""* ]]
574 echo "========================================"
576 echo "Your password contains a quote (\")"
577 echo "Your SMTP Password will not be set. You will need to edit app.yml to enter it."
578 echo "========================================"
582 if [[ "$smtp_password" == *"$SLASH"* ]]
584 if [[ "$smtp_password" == *"$SLASH"* ]]
587 if [[ "$smtp_password" == *"$SLASH"* ]]
590 echo "========================================"
592 echo "Your password contains all available delimiters (+, |, and Q). "
593 echo "Your SMTP Password will not be set. You will need to edit app.yml to enter it."
594 echo "========================================"
600 if [[ "$SLASH" != "BROKEN" ]]
602 sed -i -e "s${SLASH}^ #\?DISCOURSE_SMTP_PASSWORD:.*${SLASH} DISCOURSE_SMTP_PASSWORD: \"${smtp_password}\"${SLASH}w $changelog" $web_file
608 echo "DISCOURSE_SMTP_PASSWORD change failed."
613 echo "Enabling Let's Encrypt"
614 sed -i -e "s/^ #\?LETSENCRYPT_ACCOUNT_EMAIL:.*/ LETSENCRYPT_ACCOUNT_EMAIL: $letsencrypt_account_email/w $changelog" $web_file
619 echo "LETSENCRYPT_ACCOUNT_EMAIL change failed."
622 local src
='^ #\?- "templates\/web.ssl.template.yml"'
623 local dst
=' \- "templates\/web.ssl.template.yml"'
624 sed -i -e "s/$src/$dst/w $changelog" $web_file
627 echo "web.ssl.template.yml enabled"
630 echo "web.ssl.template.yml NOT ENABLED--was it on already?"
632 local src
='^ #\?- "templates\/web.letsencrypt.ssl.template.yml"'
633 local dst
=' - "templates\/web.letsencrypt.ssl.template.yml"'
635 sed -i -e "s/$src/$dst/w $changelog" $web_file
638 echo "letsencrypt.ssl.template.yml enabled"
641 echo "letsencrypt.ssl.template.yml NOT ENABLED -- was it on already?"
645 if [ $maxmind_license_key != "1234567890123456" ]
647 echo "Setting MAXMIND key to $maxmind_license_key in $web_file"
648 sed -i -e "s/^.*DISCOURSE_MAXMIND_LICENSE_KEY:.*/ DISCOURSE_MAXMIND_LICENSE_KEY: $maxmind_license_key/w $changelog" $web_file
653 echo "DISCOURSE_MAXMIND_LICENSE_KEY change failed."
658 if [ "$update_ok" == "y" ]
660 echo -e "\nConfiguration file at $config_file updated successfully!\n"
662 echo -e "\nUnfortunately, there was an error changing $config_file\n"
663 echo -d "This may happen if you have made unexpected changes."
669 ## is our config file valid? Does it have the required fields set?
675 for x
in DISCOURSE_SMTP_ADDRESS DISCOURSE_SMTP_USER_NAME DISCOURSE_SMTP_PASSWORD \
676 DISCOURSE_DEVELOPER_EMAILS DISCOURSE_HOSTNAME
679 local result
=$read_config_result
681 local default
=$read_default_result
683 if [ ! -z "$result" ]
685 if [[ "$config_line" = *"$default"* ]]
687 echo "$x left at incorrect default of $default"
690 config_val
=`echo $config_line | awk '{print $2}'`
691 if [ -z $config_val ]
693 echo "$x was not configured"
697 echo "$x not present"
702 if [ "$valid_config" != "y" ]; then
703 echo -e "\nSorry, these $web_file settings aren't valid -- can't continue!"
704 echo "If you have unusual requirements, edit $web_file and then: "
705 echo "./launcher bootstrap $app_name"
712 ## template file names
715 if [ "$1" == "2container" ]
719 web_template
=samples
/web_only.yml
720 data_template
=samples
/data.yml
721 web_file
=containers
/$app_name.yml
722 data_file
=containers
/$data_name.yml
726 web_template
=samples
/standalone.yml
728 web_file
=containers
/$app_name.yml
729 data_file
=containers
/$app_name.yml
731 changelog
=/tmp
/changelog
734 ## Check requirements before creating a copy of a config file we won't edit
737 check_and_install_docker
738 check_disk_and_memory
739 assert_maxmind_license_key
741 if [ -a "$web_file" ]
743 echo "The configuration file $web_file already exists!"
745 echo ". . . reconfiguring . . ."
748 DATE
=`date +"%Y-%m-%d-%H%M%S"`
749 BACKUP
=$app_name.yml.
$DATE.bak
750 echo Saving old
file as
$BACKUP
751 cp $web_file containers
/$BACKUP
752 echo "Stopping existing container in 5 seconds or Control-C to cancel."
758 cp -v $web_template $web_file
759 if [ "$data_name" == "data" ]
761 echo "--------------------------------------------------"
762 echo "This two container setup is currently unsupported. Use at your own risk!"
763 echo "--------------------------------------------------"
764 DISCOURSE_DB_PASSWORD
=`date +%s | sha256sum | base64 | head -c 20`
766 sed -i -e "s/DISCOURSE_DB_PASSWORD: SOME_SECRET/DISCOURSE_DB_PASSWORD: $DISCOURSE_DB_PASSWORD/w $changelog" $web_file
771 echo "Problem changing DISCOURSE_DB_PASSWORD" in $web_file
774 cp -v $data_template $data_file
776 sed -i -e "s/password ${quote}SOME_SECRET${quote}/password '$DISCOURSE_DB_PASSWORD'/w $changelog" $data_file
781 echo "Problem changing DISCOURSE_DB_PASSWORD" in $data_file
791 ## if we reach this point without exiting, OK to proceed
792 ## rebuild won't fail if there's nothing to rebuild and does the restart
794 echo "Updates successful. Rebuilding in 5 seconds."
795 sleep 5 # Just a chance to ^C in case they were too fast on the draw
796 if [ "$data_name" == "$app_name" ]
798 echo Building
$app_name
799 .
/launcher rebuild
$app_name
801 echo Building
$data_name now . . .
802 .
/launcher rebuild
$data_name
803 echo Building
$app_name now . . .
804 .
/launcher rebuild
$app_name