Say you have an Amazon EC2 host with TCP ports 80 and 3000 open. For the purposes of this post, we’ll run Ubuntu 16 on this EC2 host.
You want to run production and deployment versions of a React application on ports 80 and 3000, respectively, using nginx
as the web server underneath.
Even if you have port 3000 open, trying to use the default React development application with your EC2 host can raise EADDRNOTAVAIL
errors, when trying to point your web browser to the EC2 host’s public IP and development port. A little more work is necessary.
This post will walk you through setting up Nodejs, installing a blank React app, installing nginx
, configuring nginx
, and setting up the React app to support both production and deployment targets to be served through the nginx
server.
Nodejs
Installation
Install node
, npm
, and npx
from Nodejs:
$ cd ~
$ wget -qO- https://nodejs.org/dist/v11.1.0/node-v11.1.0-linux-x64.tar.xz > node-v11.1.0-linux-x64.tar.xz
$ tar xvf node-v11.1.0-linux-x64.tar.xz
...
$ cd node-v11.1.0-linux-x64/bin
$ sudo ln -s ${PWD}/node /usr/bin/node
$ sudo ln -s ${PWD}/npm /usr/bin/npm
$ sudo ln -s ${PWD}/npx /usr/bin/npx
nginx
Installation
$ sudo apt install nginx -y
Production
Throughout this post, change my-react-app
to the name of your React application.
$ sudo mkdir /var/www/my-react-app
$ sudo gpasswd -a "$USER" www-data
$ sudo chown -R "$USER":www-data /var/www
$ find /var/www -type f -exec chmod 0660 {} \;
$ sudo find /var/www -type d -exec chmod 2770 {} \;
Open a text file called /etc/nginx/sites-available/my-react-app-production
and add the following boilerplate:
server {
listen 80;
server_name 18.218.123.1;
root /var/www/my-react-app;
index index.html;
access_log /var/log/nginx/my-react-app-production.access.log;
error_log /var/log/nginx/my-react-app-production.error.log;
location / {
try_files $uri /index.html =404;
}
}
This sets up nginx
to listen to requests on port 80 and serve files from the document root /var/www/my-react-app
. Later on, we’ll show how to put the production React application into this folder. For now, we first want to get the server set up.
I am using 18.218.123.1
as a placeholder. Your EC2 host will have its own IP address. Replace 18.218.123.1
with the public IP of your EC2 host. This host IP address will be available in the AWS EC2 console.
If you have an IP name that resolves to the public IP address, you can add a second server_name
line that specifies this name, and then your web browser can point to this hostname.
Make a symbolic link to this configuration file in the /etc/nginx/sites-enabled
directory:
$ sudo ln -s /etc/nginx/sites-available/my-react-app-production /etc/nginx/sites-enabled/my-react-app-production
Development
When we set up the EC2 host, we set up the security group to allow public-facing traffic on TCP ports 80 and 3000.
For the development server, we will use nginx
as a proxy server that redirects any public-side web requests that hit port 3000, to redirect them internally to the EC2 host’s private IP address on port 8080.
We’ll show later how to configure our development React app to run on this private port assignment of 8080, but for now we start with the nginx
configuration.
Open a text file called /etc/nginx/sites-available/my-react-app-development
and add the following boilerplate:
server {
listen 3000;
server_name 18.218.123.1;
access_log /var/log/nginx/my-react-app-development.access.log;
error_log /var/log/nginx/my-react-app-development.error.log;
location / {
proxy_pass http://ip-172-31-123-1.us-east-2.compute.internal:8080;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
}
Change the line proxy_pass http://ip-172-31-123-1.us-east-2.compute.internal:8080
to replace ip-172-31-123-1.us-east-2.compute.internal
with the private IP address of your EC2 host.
As with the public IP, this private IP address is available in the AWS EC2 console.
Make a symbolic link to this configuration file in the /etc/nginx/sites-enabled
directory:
$ sudo ln -s /etc/nginx/sites-available/my-react-app-development /etc/nginx/sites-enabled/my-react-app-development
Startup
Use the following command to start the nginx
service:
$ sudo service nginx start
If you make any changes to the web server’s configuration, such as to these two site configuration files, use the following to restart the service:
$ sudo service nginx restart
React
Installation
To set up a new app, you can run the following:
$ npx create-react-app my-react-app
$ cd my-react-app
In the React application directory, create a text file called .env
and put in HOST
and PORT
settings:
HOST=0.0.0.0
PORT=8080
Development
To start the development server:
$ npm run start
This starts a development server on the EC2 host’s private-facing network on port 8080. If nginx
is running and if it is set up correctly, then you can open your web browser to the EC2 host’s public IP and specify port 3000:
Remember that we are using 18.218.123.1
as a placeholder. Replace that public IP with the one that Amazon assigned to your EC2 host.
The create-react-app
tool creates an environment where you can edit the JSX files of your React application, and the development server will rebuild the application when those files change. So you should see any updates more or less immediately!
Production
When you’re ready to deploy your application to production, there are two steps.
First, set up a deployment target in the React application’s package.json
file.
Open the package.json
file in a text editor and go to the scripts
property. Initially, it will look like this or similar:
...
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
...
To this file, add a deploy
target that synchronizes the build
folder with what nginx
has been configured to serve. The package.json
file should look something like this:
...
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject",
"deploy": "rsync -avzhe ssh --progress ./build/* /var/www/my-react-app"
},
...
You only need to set up the deploy
target once. This uses rsync
and ssh
. The use of ssh
here is optional as both source and destination are on the same local filesystem.
However, you may want to push up a compiled application to a remote host at a later time. You might simply edit the destination, specifying the username and host parameters of that remote deployment host.
Second, run the build
and deploy
targets:
$ cd my-react-app
$ npm run build
$ npm run deploy
The first target “compiles” the React application to the build
folder. The second target synchronizes the /var/www/my-react-app
directory with the contents of the build
folder.
After doing this, the contents of the public-facing web server will be available from the public IP address on port 80. If nginx
is running and is set up correctly, then you can open your web browser to the EC2 host’s public IP and specify port 80:
Remember that we are using 18.218.123.1
as a placeholder. Replace that public IP with the one that Amazon assigned to your EC2 host.
Going forwards, to deploy to production, all you have to do is build
and deploy
. The updated application will be available to web clients.