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"`
320 ## prompt user for typical Discourse config file values
322 ask_user_for_config
() {
324 # NOTE: Defaults now come from standalone.yml
326 local changelog
=/tmp
/changelog.
$PPID
327 read_config
"DISCOURSE_SMTP_ADDRESS"
328 local smtp_address
=$read_config_result
329 # NOTE: if there are spaces between emails, this breaks, but a human should be paying attention
330 read_config
"DISCOURSE_DEVELOPER_EMAILS"
331 local developer_emails
=$read_config_result
332 read_config
"DISCOURSE_SMTP_PASSWORD"
333 local smtp_password
=$read_config_result
334 read_config
"DISCOURSE_SMTP_PORT"
335 local smtp_port
=$read_config_result
336 read_config
"DISCOURSE_SMTP_USER_NAME"
337 local smtp_user_name
=$read_config_result
338 if [ "$smtp_password" = "pa$$word" ]
342 read_config
"LETSENCRYPT_ACCOUNT_EMAIL"
343 local letsencrypt_account_email
=$read_config_result
344 if [ -z $letsencrypt_account_email ]
346 letsencrypt_account_email
="me@example.com"
348 if [ "$letsencrypt_account_email" = "me@example.com" ]
350 local letsencrypt_status
="ENTER to skip"
352 local letsencrypt_status
="Enter 'OFF' to disable."
355 read_config
"DISCOURSE_HOSTNAME"
356 hostname
=$read_config_result
364 while [[ "$config_ok" == "n" ]]
366 if [ ! -z "$hostname" ]
368 read -p "Hostname for your Discourse? [$hostname]: " new_value
369 if [ ! -z "$new_value" ]
371 hostname
="$new_value"
373 if [[ $hostname =~ ^
[0-9]{1,3}\.
[0-9]{1,3}\.
[0-9]{1,3}\.
[0-9]{1,3}$
]]
376 echo "Discourse requires a DNS hostname. IP addresses are unsupported and will not work."
378 hostname
="discourse.example.com"
382 check_IP_match
$hostname
384 if [ ! -z "$developer_emails" ]
386 read -p "Email address for admin account(s)? [$developer_emails]: " new_value
387 if [ ! -z "$new_value" ]
389 developer_emails
="$new_value"
393 if [ ! -z "$smtp_address" ]
395 read -p "SMTP server address? [$smtp_address]: " new_value
396 if [ ! -z "$new_value" ]
398 smtp_address
="$new_value"
402 if [ ! -z "$smtp_port" ]
404 read -p "SMTP port? [$smtp_port]: " new_value
405 if [ ! -z "$new_value" ]
407 smtp_port
="$new_value"
412 ## automatically set correct user name based on common mail providers unless it's been set
414 if [ "$smtp_user_name" == "user@example.com" ]
416 if [ "$smtp_address" == "smtp.sparkpostmail.com" ]
418 smtp_user_name
="SMTP_Injection"
420 if [ "$smtp_address" == "smtp.sendgrid.net" ]
422 smtp_user_name
="apikey"
424 if [ "$smtp_address" == "smtp.mailgun.org" ]
426 smtp_user_name
="postmaster@$hostname"
430 if [ ! -z "$smtp_user_name" ]
432 read -p "SMTP user name? [$smtp_user_name]: " new_value
433 if [ ! -z "$new_value" ]
435 smtp_user_name
="$new_value"
439 read -p "SMTP password? [$smtp_password]: " new_value
440 if [ ! -z "$new_value" ]
442 smtp_password
="$new_value"
445 if [ ! -z $letsencrypt_account_email ]
447 read -p "Optional email address for Let's Encrypt warnings? ($letsencrypt_status) [$letsencrypt_account_email]: " new_value
448 if [ ! -z "$new_value" ]
450 letsencrypt_account_email
="$new_value"
451 if [ "${new_value,,}" = "off" ]
453 letsencrypt_status
="ENTER to skip"
455 letsencrypt_status
="Enter 'OFF' to disable."
460 echo -e "\nDoes this look right?\n"
461 echo "Hostname : $hostname"
462 echo "Email : $developer_emails"
463 echo "SMTP address : $smtp_address"
464 echo "SMTP port : $smtp_port"
465 echo "SMTP username : $smtp_user_name"
466 echo "SMTP password : $smtp_password"
468 if [ "$letsencrypt_status" == "Enter 'OFF' to disable." ]
470 echo "Let's Encrypt : $letsencrypt_account_email"
475 read -p "ENTER to continue, 'n' to try again, Ctrl+C to exit: " config_ok
478 sed -i -e "s/^ DISCOURSE_HOSTNAME:.*/ DISCOURSE_HOSTNAME: $hostname/w $changelog" $web_file
483 echo "DISCOURSE_HOSTNAME change failed."
487 sed -i -e "s/^ DISCOURSE_DEVELOPER_EMAILS:.*/ DISCOURSE_DEVELOPER_EMAILS: \'$developer_emails\'/w $changelog" $web_file
492 echo "DISCOURSE_DEVELOPER_EMAILS change failed."
496 sed -i -e "s/^ DISCOURSE_SMTP_ADDRESS:.*/ DISCOURSE_SMTP_ADDRESS: $smtp_address/w $changelog" $web_file
501 echo "DISCOURSE_SMTP_ADDRESS change failed."
505 sed -i -e "s/^ #\?DISCOURSE_SMTP_PORT:.*/ DISCOURSE_SMTP_PORT: $smtp_port/w $changelog" $web_file
510 echo "DISCOURSE_SMTP_PORT change failed."
514 sed -i -e "s/^ #\?DISCOURSE_SMTP_USER_NAME:.*/ DISCOURSE_SMTP_USER_NAME: $smtp_user_name/w $changelog" $web_file
519 echo "DISCOURSE_SMTP_USER_NAME change failed."
523 if [[ "$smtp_password" == *"\""* ]]
526 echo "========================================"
528 echo "Your password contains a quote (\")"
529 echo "Your SMTP Password will not be set. You will need to edit app.yml to enter it."
530 echo "========================================"
534 if [[ "$smtp_password" == *"$SLASH"* ]]
536 if [[ "$smtp_password" == *"$SLASH"* ]]
539 if [[ "$smtp_password" == *"$SLASH"* ]]
542 echo "========================================"
544 echo "Your password contains all available delimiters (+, |, and Q). "
545 echo "Your SMTP Password will not be set. You will need to edit app.yml to enter it."
546 echo "========================================"
552 if [[ "$SLASH" != "BROKEN" ]]
554 sed -i -e "s${SLASH}^ #\?DISCOURSE_SMTP_PASSWORD:.*${SLASH} DISCOURSE_SMTP_PASSWORD: \"${smtp_password}\"${SLASH}w $changelog" $web_file
560 echo "DISCOURSE_SMTP_PASSWORD change failed."
565 echo "Enabling Let's Encrypt"
566 sed -i -e "s/^ #\?LETSENCRYPT_ACCOUNT_EMAIL:.*/ LETSENCRYPT_ACCOUNT_EMAIL: $letsencrypt_account_email/w $changelog" $web_file
571 echo "LETSENCRYPT_ACCOUNT_EMAIL change failed."
574 local src
='^ #\?- "templates\/web.ssl.template.yml"'
575 local dst
=' \- "templates\/web.ssl.template.yml"'
576 sed -i -e "s/$src/$dst/w $changelog" $web_file
579 echo "web.ssl.template.yml enabled"
582 echo "web.ssl.template.yml NOT ENABLED--was it on already?"
584 local src
='^ #\?- "templates\/web.letsencrypt.ssl.template.yml"'
585 local dst
=' - "templates\/web.letsencrypt.ssl.template.yml"'
587 sed -i -e "s/$src/$dst/w $changelog" $web_file
590 echo "letsencrypt.ssl.template.yml enabled"
593 echo "letsencrypt.ssl.template.yml NOT ENABLED -- was it on already?"
596 if [ "$update_ok" == "y" ]
598 echo -e "\nConfiguration file at $config_file updated successfully!\n"
600 echo -e "\nUnfortunately, there was an error changing $config_file\n"
601 echo -d "This may happen if you have made unexpected changes."
607 ## is our config file valid? Does it have the required fields set?
613 for x
in DISCOURSE_SMTP_ADDRESS DISCOURSE_SMTP_USER_NAME DISCOURSE_SMTP_PASSWORD \
614 DISCOURSE_DEVELOPER_EMAILS DISCOURSE_HOSTNAME
617 local result
=$read_config_result
619 local default
=$read_default_result
621 if [ ! -z "$result" ]
623 if [[ "$config_line" = *"$default"* ]]
625 echo "$x left at incorrect default of $default"
628 config_val
=`echo $config_line | awk '{print $2}'`
629 if [ -z $config_val ]
631 echo "$x was not configured"
635 echo "$x not present"
640 if [ "$valid_config" != "y" ]; then
641 echo -e "\nSorry, these $web_file settings aren't valid -- can't continue!"
642 echo "If you have unusual requirements, edit $web_file and then: "
643 echo "./launcher bootstrap $app_name"
650 ## template file names
653 if [ "$1" == "2container" ]
657 web_template
=samples
/web_only.yml
658 data_template
=samples
/data.yml
659 web_file
=containers
/$app_name.yml
660 data_file
=containers
/$data_name.yml
664 web_template
=samples
/standalone.yml
666 web_file
=containers
/$app_name.yml
667 data_file
=containers
/$app_name.yml
669 changelog
=/tmp
/changelog
672 ## Check requirements before creating a copy of a config file we won't edit
675 check_and_install_docker
676 check_disk_and_memory
678 if [ -a "$web_file" ]
680 echo "The configuration file $web_file already exists!"
682 echo ". . . reconfiguring . . ."
685 DATE
=`date +"%Y-%m-%d-%H%M%S"`
686 BACKUP
=$app_name.yml.
$DATE.bak
687 echo Saving old
file as
$BACKUP
688 cp $web_file containers
/$BACKUP
689 echo "Stopping existing container in 5 seconds or Control-C to cancel."
695 cp -v $web_template $web_file
696 if [ "$data_name" == "data" ]
698 echo "--------------------------------------------------"
699 echo "This two container setup is currently unsupported. Use at your own risk!"
700 echo "--------------------------------------------------"
701 DISCOURSE_DB_PASSWORD
=`date +%s | sha256sum | base64 | head -c 20`
703 sed -i -e "s/DISCOURSE_DB_PASSWORD: SOME_SECRET/DISCOURSE_DB_PASSWORD: $DISCOURSE_DB_PASSWORD/w $changelog" $web_file
708 echo "Problem changing DISCOURSE_DB_PASSWORD" in $web_file
711 cp -v $data_template $data_file
713 sed -i -e "s/password ${quote}SOME_SECRET${quote}/password '$DISCOURSE_DB_PASSWORD'/w $changelog" $data_file
718 echo "Problem changing DISCOURSE_DB_PASSWORD" in $data_file
728 ## if we reach this point without exiting, OK to proceed
729 ## rebuild won't fail if there's nothing to rebuild and does the restart
731 echo "Updates successful. Rebuilding in 5 seconds."
732 sleep 5 # Just a chance to ^C in case they were too fast on the draw
733 if [ "$data_name" == "$app_name" ]
735 echo Building
$app_name
736 .
/launcher rebuild
$app_name
738 echo Building
$data_name now . . .
739 .
/launcher rebuild
$data_name
740 echo Building
$app_name now . . .
741 .
/launcher rebuild
$app_name