</svg> <p class="callout__text"> If you head to webapp.xyz right now, you will get "403 Forbidden". In the case of CentOS, you need to run the code below
centos@remote:~$ sudo chcon -vR system_u:object_r:httpd_sys_content_t:s0 /var/www/webapp.xyz/ Now you will see "Success! Your NGINX server is successfully configured for webapp.xyz".
Setting Up The Application Prepare the Rust compiler.
debian@remote:~$ # Debian debian@remote:~$ sudo apt -y install gcc && curl https://sh.rustup.rs -sSf | sh -s -- -y centos@remote:~$ # CentOS centos@remote:~$ sudo dnf -y install gcc && curl https://sh.rustup.rs -sSf | sh -s -- -y debian@remote:~$ source $HOME /.cargo/env If you get curl: command not found. You need to install it.
debian@remote:~$ # Debian debian@remote:~$ sudo apt -y install curl centos@remote:~$ # CentOS centos@remote:~$ sudo dnf -y install curl Create a directory for the application.
debian@remote:~$ sudo mkdir -p /opt/webapp && sudo chown -R $USER : /opt/webapp Upload your source code to the remote machine.
debian@local:~$ # In local machine! Notice the username in the prompt. debian@local:~$ cd /code/webapp debian@local:~$ rsync -avrzP --exclude 'target/' -e "ssh -i ~vm-key.pem" . centos@10.10.10.10:/opt/webapp If you encounter a similar error below. Make sure rsync is installed on your remote machine.
bash: rsync: command not found rsync: connection unexpectedly closed (0 bytes received so far ) [ sender ] rsync error: error in rsync protocol data stream (code 12 ) at io.c( 228 ) [ sender = 3.2.3 ] Build the application.
debian@remote:~$ cd /opt/webapp debian@remote:~$ cargo build --release debian@remote:~$ # export the environment variable if you had one debian@remote:~$ source .env Run the application ~/opt/webapp/target/release/webapp &. Make sure it runs well by hitting its endpoint in your remote machine.
debian@remote:~$ curl 127.0.0.1:3000/health { "data" : { " status":"Running"}} Make every request to NGINX forwarded to our web application.
server_name webapp.xyz www.webapp.xyz; location / { - try_files $uri $uri/ =404; + proxy_pass http://127.0.0.1:3000; } } Make sure everything is OK with sudo nginx -t and restart the NGINX. Check if your web application runs well by doing the request to port 80. You can omit the port, as it is the default.
debian@remote:~$ curl 127.0.0.1:80/health debian@remote:~$ curl 127.0.0.1/health If this doesn't work, comment out this line in /etc/nginx/nginx.conf
include /etc/nginx/conf.d/*.conf; + #include /etc/nginx/sites-enabled/*; Now try to do a request from a local machine, to make sure it works from an external request. Ignore the jq command, it is just there to prettify the JSON output.
debian@remote:~$ curl --location 10.10.10.10/health | jq { "data" : { "status" : "Running" } } Stop the running application using kill or killall. Now we will use the systemd unit to manage the application. This gives us better control and persistent logging.
Put the code below in /etc/systemd/system/webapp.xyz.service. Change the ExecStart to your binary location.
</svg> <p class="callout__text"> Avoid putting comments in systemd unit file, as it causes unexpected behavior.
[Unit] Description=webapp [Service] User=debian Group=debian WorkingDirectory=/opt/webapp Environment="LOG_LEVEL=critical" ExecStart=/opt/webapp/target/release/webapp [Install] WantedBy=multi-user.target If you are using CentOS, change User and Group to 'centos' (without the quotes). Maximize the use of ROCKET_* If you are using Rocket.
Environment="ROCKET_LOG_LEVEL=critical" Now you can use systemctl command to manage the application and journalctl to see the logs.
debian@remote:~$ sudo systemctl start webapp.xyz.service debian@remote:~$ sudo systemctl status webapp.xyz.service ● webapp.xyz.service - webapp Loaded: loaded (/etc/systemd/system/webapp.xyz.service ; disabled ; vendor preset: enabled ) Active: active (running) since Thu 2022-08-18 01:57:37 UTC ; 1s ago debian@remote:~$ journalctl -u webapp.xyz.service Aug 18 01:57:37 user systemd[1]: Started webapp. Aug 18 01:57:37 user webapp[31351]: listening on 127.0.0.1:3000 Make sure it works the same as manual invocation.
debian@local:~$ curl http://10.10.10.10:80/health debian@local:~$ curl http://webapp.xyz:80/health
</svg> <p class="callout__text"> Sometimes you get unexpected behavior. Don't panic!. You can always inspect the log.
If you get "Bad Gateway" from the curl result. Always consult the NGINX log.
debian@remote:~$ sudo tail -F /var/log/nginx/error.log In my case, I got "Permission denied".
2021/05/26 00:06:11 [crit] 21856#0: *1 connect() to 0.0.0.0:3000 failed (13: Permission denied) while connecting to upstream, client: 114.125.113.56, server: webapp.xyz, request: "GET /health HTTP/1.1", upstream: "http://0.0.0.0:3000/health", host: "webapp.Turns" The issue is related to SELinux (CentOS) as described in this answer
centos@remote:~$ sudo setsebool -P httpd_can_network_connect 1 Setting Up The SSL Install the certbot.
debian@remote:~$ sudo dnf install -y epel-release debian@remote:~$ sudo dnf install -y certbot python3-certbot-nginx Run the certbot.
debian@remote:~$ sudo certbot --nginx -d anymid.xyz -d www.anymid.xyz Test if the SSL works.
debian@remote:~$ curl https://anymid.xyz/health/ # {"data":{"status":"running"}} Renew SSL Certificate Automatically It will be a huge pain if we renew the certificate manually all the time. Let the cron handle this.
Add the code below to cron using sudo crontab -e.
0 0,12 * * * python3 -c 'import random; import time; time.sleep(random.random() * 3600)' && certbot renew --quiet debian@remote:~$ sudo crontab -e # then paste the code above Check if the task was inserted successfully.
debian@remote:~$ sudo crontab -l 0 0,12 * * * python3 -c 'import random; import time; time.sleep(random.random() * 3600)' && certbot renew --quiet The code above will run in the afternoon and at midnight every day. The random section will select a random minute within the hour for the renewal.
Notes Forwarding the header from NGINX to the application is different in Axum and Rocket. In Axum, I can get the header out of the box. But in Rocket, I need to add some solutions . I use the following configurations in my Rocket application.
location / { + proxy_set_header Host $http_host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header X-NginX-Proxy true; + proxy_redirect off; proxy_pass http://0.0.0.0:8000; } The initial article is using Rocket and was written a year ago. The improved version is using Axum and is written today. So take the time difference into the account too.
If you liked this article, please support my work . It will definitely be rewarding and motivating. Thanks for the support!
</svg> Credits