Skip to content

Instantly share code, notes, and snippets.

@htdvisser
Last active June 5, 2022 11:38
Show Gist options
  • Star 6 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save htdvisser/4503f30699308a7fd9e0aa3ebc2f3eb4 to your computer and use it in GitHub Desktop.
Save htdvisser/4503f30699308a7fd9e0aa3ebc2f3eb4 to your computer and use it in GitHub Desktop.
The Things Stack Bootstrap

The Things Stack Bootstrap

A script to bootstrap The Things Stack, based on the getting started guide.

Preparation

  • Clone this gist.
  • Spin up a fresh Ubuntu 18.04 server.
    • Var SSHUser: the username that will be used to SSH into the server. This user must be able to sudo.
  • Point a public DNS record to your server's IP address. It may take some time before this resolves.
    • Var Host: the DNS record.
  • Try to remember your email address.
    • Var AdminEmail: your email address.

Usage

$ SSHUser=ubuntu Host=thethings.example.com AdminEmail=you@example.com ./bootstrap.sh

Next Steps

  • This script does not configure all options. Read the documentation to learn more about the available options.
  • This script is not intended for production servers. You don't want to use the latest tag of the Docker images. You don't want a single instance of Cockroach. You don't want a single instance of Redis. You don't want to store your blobs on a local disk.

Support

No support on this script; help yourself.

#!/usr/bin/env bash
# Initial Variables:
Host=${Host:-thethings.example.com}
SSHUser=${SSHUser:-ubuntu}
AdminEmail=${AdminEmail:-admin@thethings.example.com}
# Derive Variables:
NetworkEmail="noreply@${Host}"
SSH=${SSHUser}@${Host}
# Generate variables if not already generated:
if [[ -f .generated ]]; then source .generated; fi
CookeyHashKey=${CookeyHashKey:-$(openssl rand -hex 64)}
CookeyBlockKey=${CookeyBlockKey:-$(openssl rand -hex 32)}
DebugPassword=${DebugPassword:-$(openssl rand -hex 16)}
ConsoleClientSecret=${ConsoleClientSecret:-$(openssl rand -hex 32)}
cat > .generated <<EOF
CookeyHashKey=${CookeyHashKey}
CookeyBlockKey=${CookeyBlockKey}
DebugPassword=${DebugPassword}
ConsoleClientSecret=${ConsoleClientSecret}
EOF
# Build .env file:
cat > .env <<EOF
TTN_LW_IS_DATABASE_URI=postgres://root@cockroach:26257/ttn_lorawan?sslmode=disable
TTN_LW_REDIS_ADDRESS=redis:6379
TTN_LW_CACHE_SERVICE=redis
TTN_LW_CACHE_REDIS_ADDRESS=redis:6379
TTN_LW_EVENTS_BACKEND=redis
TTN_LW_EVENTS_REDIS_ADDRESS=redis:6379
TTN_LW_TLS_SOURCE=acme
TTN_LW_TLS_ACME_DIR=/var/lib/acme
TTN_LW_TLS_ACME_EMAIL=${AdminEmail}
TTN_LW_TLS_ACME_HOSTS=${Host}
TTN_LW_TLS_ACME_DEFAULT_HOST=${Host}
TTN_LW_HTTP_COOKIE_HASH_KEY=${CookeyHashKey}
TTN_LW_HTTP_COOKIE_BLOCK_KEY=${CookeyBlockKey}
TTN_LW_HTTP_METRICS_PASSWORD=${DebugPassword}
TTN_LW_HTTP_PPROF_PASSWORD=${DebugPassword}
TTN_LW_IS_EMAIL_SENDER_NAME=The Things Stack
TTN_LW_IS_EMAIL_SENDER_ADDRESS=noreply@${Host}
TTN_LW_IS_EMAIL_NETWORK_CONSOLE_URL=https://${Host}/console
TTN_LW_IS_EMAIL_NETWORK_IDENTITY_SERVER_URL=https://${Host}/oauth
# Intentionally omitting email provider config
TTN_LW_IS_OAUTH_UI_CANONICAL_URL=https://${Host}/oauth
TTN_LW_IS_OAUTH_UI_IS_BASE_URL=https://${Host}/api/v3
TTN_LW_CONSOLE_OAUTH_AUTHORIZE_URL=https://${Host}/oauth/authorize
TTN_LW_CONSOLE_OAUTH_TOKEN_URL=https://${Host}/oauth/token
TTN_LW_CONSOLE_OAUTH_LOGOUT_URL=https://${Host}/oauth/logout
TTN_LW_CONSOLE_UI_CANONICAL_URL=https://${Host}/console
TTN_LW_CONSOLE_UI_AS_BASE_URL=https://${Host}/api/v3
TTN_LW_CONSOLE_UI_GS_BASE_URL=https://${Host}/api/v3
TTN_LW_CONSOLE_UI_GCS_BASE_URL=https://${Host}/api/v3
TTN_LW_CONSOLE_UI_IS_BASE_URL=https://${Host}/api/v3
TTN_LW_CONSOLE_UI_JS_BASE_URL=https://${Host}/api/v3
TTN_LW_CONSOLE_UI_NS_BASE_URL=https://${Host}/api/v3
TTN_LW_CONSOLE_UI_EDTC_BASE_URL=https://${Host}/api/v3
TTN_LW_CONSOLE_UI_QRG_BASE_URL=https://${Host}/api/v3
TTN_LW_CONSOLE_OAUTH_CLIENT_ID=console
TTN_LW_CONSOLE_OAUTH_CLIENT_SECRET=${ConsoleClientSecret}
TTN_LW_GS_REQUIRE_REGISTERED_GATEWAYS=true
TTN_LW_GS_MQTT_V2_PUBLIC_ADDRESS=${Host}:1881
TTN_LW_GS_MQTT_V2_PUBLIC_TLS_ADDRESS=${Host}:8881
TTN_LW_GS_MQTT_PUBLIC_ADDRESS=${Host}:1882
TTN_LW_GS_MQTT_PUBLIC_TLS_ADDRESS=${Host}:8882
TTN_LW_GS_BASIC_STATION_USE_TRAFFIC_TLS_ADDRESS=true
TTN_LW_AS_MQTT_PUBLIC_ADDRESS=${Host}:1883
TTN_LW_AS_MQTT_PUBLIC_TLS_ADDRESS=${Host}:8883
TTN_LW_AS_WEBHOOKS_DOWNLINK_PUBLIC_ADDRESS=http://${Host}/api/v3
TTN_LW_AS_WEBHOOKS_DOWNLINK_PUBLIC_TLS_ADDRESS=https://${Host}/api/v3
TTN_LW_GCS_BASIC_STATION_DEFAULT_LNS_URI=wss://${Host}:8887
TTN_LW_GCS_THE_THINGS_GATEWAY_DEFAULT_MQTT_SERVER=mqtts://${Host}:8881
EOF
# Prepare the server:
scp prepare.sh ${SSH}:/tmp/prepare.sh
ssh ${SSH} chmod +x /tmp/prepare.sh
ssh ${SSH} /tmp/prepare.sh
# Prepare the app:
scp docker-compose.yml ${SSH}:/app/the-things-stack/docker-compose.yml
scp .env ${SSH}:/app/the-things-stack/.env
function remote-docker-compose() {
ssh -t ${SSH} docker-compose -f /app/the-things-stack/docker-compose.yml $@
}
remote-docker-compose pull
remote-docker-compose run --rm stack is-db init
remote-docker-compose run --rm stack is-db create-admin-user \
--id admin \
--email ${AdminEmail}
remote-docker-compose run --rm stack is-db create-oauth-client \
--id cli \
--name "Command Line Interface" \
--owner admin \
--no-secret \
--redirect-uri "local-callback" \
--redirect-uri "code"
remote-docker-compose run --rm stack is-db create-oauth-client \
--id console \
--name "Console" \
--owner admin \
--secret ${ConsoleClientSecret} \
--redirect-uri "https://${Host}/console/oauth/callback" \
--redirect-uri "/console/oauth/callback" \
--logout-redirect-uri "https://${Host}/console" \
--logout-redirect-uri "/console"
remote-docker-compose up -d
cat > config.yml <<EOF
credentials-id: ${Host}
oauth-server-address: https://${Host}/oauth
identity-server-grpc-address: ${Host}:8884
gateway-server-grpc-address: ${Host}:8884
network-server-grpc-address: ${Host}:8884
application-server-grpc-address: ${Host}:8884
join-server-grpc-address: ${Host}:8884
device-claiming-server-grpc-address: ${Host}:8884
device-template-converter-grpc-address: ${Host}:8884
qr-code-generator-grpc-address: ${Host}:8884
EOF
export TTN_LW_CONFIG="${PWD}/config.yml"
echo "Deployment: https://${Host}"
echo "Documentation: https://${Host}/assets/doc/"
echo "CLI configuration file: config.yml"
echo ""
echo "To use the CLI, run:"
echo ""
echo "export TTN_LW_CONFIG=\"${PWD}/config.yml\""
version: "3.7"
services:
cockroach:
image: 'cockroachdb/cockroach:latest' # TODO: Tag for production.
command: 'start --http-port 26256 --insecure'
restart: 'unless-stopped'
volumes:
- './data/cockroach:/cockroach/cockroach-data'
redis:
image: 'redis:latest' # TODO: Tag for production.
command: 'redis-server --appendonly yes'
restart: 'unless-stopped'
volumes:
- './data/redis:/data'
stack:
image: 'thethingsnetwork/lorawan-stack:latest' # TODO: Tag for production.
entrypoint: 'ttn-lw-stack'
command: 'start'
restart: 'unless-stopped'
depends_on:
- 'cockroach'
- 'redis'
volumes:
- './acme:/var/lib/acme'
- './data/blob:/srv/ttn-lorawan/public/blob'
ports:
- '80:1885'
- '443:8885'
- '1881:1881'
- '8881:8881'
- '1882:1882'
- '8882:8882'
- '1883:1883'
- '8883:8883'
- '1884:1884'
- '8884:8884'
- '1887:1887'
- '8887:8887'
- '1700:1700/udp'
env_file: '.env'
#!/usr/bin/env bash
set -e
# Install Docker:
sudo apt-get update
sudo apt-get -y upgrade
sudo apt-get -y install \
apt-transport-https \
ca-certificates \
curl \
gnupg-agent \
software-properties-common
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
sudo add-apt-repository \
"deb [arch=amd64] https://download.docker.com/linux/ubuntu \
$(lsb_release -cs) \
stable"
sudo apt-get update
sudo apt-get install docker-ce docker-ce-cli containerd.io
sudo usermod -aG docker $USER
# Install Docker Compose:
sudo curl -L "https://github.com/docker/compose/releases/download/1.25.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose
# Prepare The Things Stack folder:
if [[ ! -d /app/the-things-stack ]];
then
sudo mkdir -p /app/the-things-stack
sudo chown $USER:$USER /app/the-things-stack
fi
cd /app/the-things-stack
if [[ ! -d ./acme ]];
then
sudo mkdir ./acme
sudo chown 886:886 ./acme
fi
@sam-higton
Copy link

Thanks for this, it really helped us set things up. I needed to add sudo usermod -aG docker ${USER} to the prepare.sh file in order to get docker-compose working, but after that it worked a charm.

@htdvisser
Copy link
Author

Added that 🙂

@sebastianbuettrich
Copy link

thanks for sharing this great script -
in
bootstrap.sh:77
i think you want to add a line

TTN_LW_CONSOLE_OAUTH_LOGOUT_URL=https://${Host}/oauth/logout

else no logout

@htdvisser
Copy link
Author

Thanks for the tip, @sebastianbuettrich! I added it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment