This guide assumes you are a CHT Core developer wanting to run the CHT Core from source code to make commits to the public GitHub repository. To set up a your environment for developing apps, see the app guide.
To deploy the CHT in production, see either AWS hosting or Self hosting
This CHT Core developer guide will have you install NodeJS, npm, Grunt and CouchDB (via Docker) on your local workstation. These instructions should work verbatim on Ubuntu 18-22, but will need tweaks for MacOS (via brew
) or Windows (via WSL2).
First, update your current Ubuntu packages and install some supporting tools via apt
:
sudo apt update && sudo apt -y dist-upgrade
sudo apt -y install xsltproc curl uidmap jq python2 git
Then install nvm
, add it to your path and install NodeJS 12:
export nvm_version=`curl -s https://api.github.com/repos/nvm-sh/nvm/releases/latest | jq -r .name`
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/$nvm_version/install.sh | $0
. ~/.$0rc
nvm install 12
Now let’s ensure NodeJS 12 and npm 6 were installed. This should output version 12.x.x for NodeJS and 6.x.x for npm
:
node -v && npm -v
With NodeJS out of the way, let’s install grunt
via npm
:
npm install -g grunt-cli
Install Docker:
curl -fsSL get.docker.com -o get-docker.sh && sh get-docker.sh
It’s easier if you don’t always have to run sudo
for all your Docker calls, so let’s set that up:
dockerd-rootless-setuptool.sh install
echo "export PATH=/usr/bin:$PATH" >> ~/.$0rc
echo "export DOCKER_HOST=unix:///run/user/1000/docker.sock" >> ~/.$0rc
. ~/.$0rc
In order for Docker to boot correctly, restart entire machine, which will complete the “Install” Section:
sudo reboot
Before we get started, let’s run the simple hello-world
Docker container. This will ensure docker is working as expected and output “Hello from Docker!” as well as some other intro text:
docker run hello-world
Now that we know Docker is set up, let’s start our CouchDB container. Note this will run in the background and store its data in /home/YOUR-USER/cht-docker
. The login for your CHT instance will be medic
and the password will be password
:
docker run -d -p 5984:5984 -p 5986:5986 --name medic-couchdb -e COUCHDB_USER=medic -e COUCHDB_PASSWORD=password -v ~/cht-docker/local.d:/opt/couchdb/data -v ~/cht-docker/local.d:/opt/couchdb/etc/local.d apache/couchdb:2
Let’s ensure CouchDB is set up with a test curl
call. This should show “nonode@nohost” in JSON:
curl -X GET "http://medic:password@localhost:5984/_membership" | jq
Every time you run any grunt
or node
commands, it will expect COUCH_NODE_NAME
and COUCH_URL
environment variables to be set:
echo "export COUCH_NODE_NAME=nonode@nohost">> ~/.$0rc
echo "export COUCH_URL=http://medic:password@localhost:5984/medic">> ~/.$0rc
. ~/.$0rc
To ensure these to exports and sourcing your rc file worked, echo the values back out. You should see nonode@nohost
and http://medic:password@localhost:5984/medic
:
echo $COUCH_NODE_NAME && echo $COUCH_URL
Clone the main CHT Core repo from GitHub and change directories into it:
git clone https://github.com/medic/cht-core ~/cht-core
cd ~/cht-core
Install dependencies and perform other setup tasks via an npm
command. Note this command may take many minutes. Be patient!
npm ci
We need to harden couch with a grunt
call, required even in development:
grunt secure-couchdb
curl -X PUT "http://medic:password@localhost:5984/_node/$COUCH_NODE_NAME/_config/httpd/WWW-Authenticate" -d '"Basic realm=\"administrator\""' -H "Content-Type: application/json"
Now you have everything installed and can begin development! You’ll need three separate terminals when doing development. In the first terminal, run:
cd ~/cht-core && grunt
Be very patient until you see:
“Waiting…”
Then in a 2nd terminal run:
cd ~/cht-core && grunt dev-api
Finally, in a 3rd terminal run:
cd ~/cht-core && grunt dev-sentinel
That’s it! Now when you edit code in your IDE, it will automatically reload. You can see the CHT running locally here: http://localhost:5988/
When you’re done with development you can ctrl + c
in the three terminals and stop the CouchDB container with docker stop medic-couchdb
. When you want to resume development later, run docker start medic-couchdb
and re-run the three terminal commands.
If you weren’t able to follow the happy path above, here are some details about the developer install that may help you troubleshoot what went wrong.
If you had issues with following the above steps, check out these links for how to install the prerequisites on your specific platform:
nvm
](npm 6.x.x)Breaking down the command from the above section, here’s a generic version that doesn’t include hard coded paths:
docker run -d -p 5984:5984 -p 5986:5986 --name medic-couchdb -e COUCHDB_USER=medic -e COUCHDB_PASSWORD=password -v <data path>:/opt/couchdb/data -v <config path>:/opt/couchdb/etc/local.d apache/couchdb:2
Parts of the command:
--name
creates a container called medic-couchdb
. You can name it whatever you want, but this is how you refer to it later-e
sets an environment variable inside the container. Two are set here, for a user and password for the initial admin user.-v
maps where couchdb stores data to your local file system to ensure persistence without depending on the container, using the path before the :
(the path after the colon is the internal path inside the docker image). This should be somewhere you have write access to, and want this data to be stored. The second mounted volume is for the couch configuration, which will retain settings if your container is removed. This is especially important after running the command to secure the instance (done in steps below).apache/couchdb:2
will install the latest package for CouchDB 2.xOnce this downloads and starts, you will need to initialise CouchDB as noted in their install instructions.
You can use docker stop medic-couchdb
to stop it and docker start medic-couchdb
to start it again. Remember that you’ll need to start it whenever you restart your OS, which might not be the case if you use a normal OS package. docker rm medic-couchdb
will totally remove the container.
Medic recommends you familiarise yourself with other Docker commands to make docker image and container management clearer.
Medic needs the following environment variables to be declared:
COUCH_URL
: the full authenticated url to the medic
DB. Locally this would be http://myadminuser:myadminpass@localhost:5984/medic
COUCH_NODE_NAME
: the name of your CouchDB’s node. The Docker image default is nonode@nohost
. Other installations may use couchdb@127.0.0.1
. You can find out by querying CouchDB’s membership APIAPI_PORT
: the port API will run on. If not defined we use 5988
CHROME_BIN
: only required if grunt unit
or grunt e2e
complain that they can’t find Chrome or if you want to run a specific version of the Chrome webdriver.How to permanently define environment variables depends on your OS and shell (e.g. for bash you can put them ~/.bashrc
). You can temporarily define them with export
:
export COUCH_NODE_NAME=nonode@nohost
export COUCH_URL=http://myadminuser:myadminpass@localhost:5984/medic
Refer to the testing doc in the GitHub repo.
nginx-local-ip
is a local proxy that keeps all traffic local, and runs without latency or throttling. If sharing your local CHT instance is not required, it is the recommended method to add a valid SSL certificate (rather than ngrok
or similar).
git clone https://github.com/medic/nginx-local-ip.git
cd
into the new directory: cd nginx-local-ip
192.168.0.3
, start nginx-local-ip
to connect to:grunt
or horti
, execute APP_URL=http://192.168.0.3:5988 docker compose up
and then access it at https://192-168-0-3.my.local-ip.co/docker
, the ports are remapped, so execute HTTP=8080 HTTPS=8443 APP_URL=https://192.168.0.3 docker compose up
and then access it at https://192-168-0-3.my.local-ip.co:8443/80
/443
) need to accept traffic from the IP address of your host machine and your local webapp port (e.g. 5988
) needs to accept traffic from the IP address of the nginx-local-ip
container (on the Docker network). If you are using the UFW firewall (in a Linux environment) you can allow traffic on these ports with the following commands:(Since local IP addresses can change over time, ranges are used in these rules so that the firewall configuration does not have to be updated each time a new address is assigned.)
$ sudo ufw allow proto tcp from 192.168.0.0/16 to any port 80,443
$ sudo ufw allow proto tcp from 172.16.0.0/16 to any port 5988
ngrok
and pagekite
are remote proxies that route local traffic between your client and the CHT via a remote SSL terminator. While easy and handy, they introduce latency and are sometimes throttled. Always use nginx-local-ip
when you need a TLS certificate and only use these when you need to share your dev instance.
ngrok
to connect to:grunt
or horti
, execute ./ngrok http 5988
docker
, execute ./ngrok http 443
https://YOUR-NGROK-NAME.ngrok.io
, replacing YOUR-NGROK-NAME
with what you signed up with).Note: The service worker cache preload sometimes fails due to connection throttling (thereby causing an ngrok
failure at startup).
YOUR-PAGEKIT-NAME
with the URL you signed up for) to connect to:grunt
or horti
, execute python pagekite.py 5988 YOUR-PAGEKIT-NAME.pagekite.me
docker
, execute python pagekite.py 443 YOUR-PAGEKIT-NAME.pagekite.me
https://YOUR-PAGEKIT-NAME.pagekite.me
).Was this page helpful?
Glad to hear it! Please tell us how we can improve.
Sorry to hear that. Please tell us how we can improve.