Introduction There are circumstances when your local machine is not suitable for heavy processing or its operating system is incompatible with your target environment. In this case, your need to process your source code in an environment that is different than your local machine. This article examines the use of remote development server using DigitalOcean. There is nothing new in having data stored in one system and having it processed in another. While conceptually everything is very clear, the implementation details can greatly vary. This article focuses on the so-called conventional server where there is no abstract layer between the OS itself and the tools used to process source code. High level overview: Unfortunately, Hackernoon does not allow custom CSS and JS and as a result, some of the custom formatting and styling is missing. If you want to read the article with its original looks, you may do so . here The setup will consist of the following: DigitalOcean Ubuntu 20.04 LTS OpenSSH Node Nginx rsync fswatch Create Droplet Sign-up on DigitalOcean and create a droplet. Choose a password-based authentication as we will configure SSH keys later in this article. If you choose to use the SSH key, you would need to manually set root password otherwise you would require . a root password reset As mentioned in the beginning, choose Ubuntu distro version 20.04 LTS (why Docker was not chosen is explained below). Choose droplet specs according to your workload needs. Other settings can be left with their default value. Choose a data center close to your location otherwise you might . physical encounter poor SSH performance By default, your Ubuntu droplet starts with a root user. You may use it to configure the OS initially. Alternatively, you can grant to your non-root user to complete the configuration. sudo Afterward, a non-root user should be used for development operations. . It is a very bad practice to use a root user as an everyday user in your development server Update your Ubuntu installation after it is up by running: apt-get update && apt-get -y upgrade Docker Initially, I was planning on using Docker for this project. However, I ran into one specific technical detail: Docker containers exit on non-zero error. In the scope of this project, this presented a difficulty in keeping Docker containers running as, for example, a linting tool would exit with a non-zero exit code if there was an issue. While it is very possible to find a solution and keep Docker containers running, I had no hard requirement to use docker. Hence I have decided to create a "conventional" or "traditional" development server. Non-Root User We will create a non-root user which we will use to sync source code from our local machine to remote. We will also use the same user to execute any developer tools or scripts. As mentioned in the beginning, using root for everyday work is a very bad habit ( ). example explanation To create a new user, simply run: adduser --home /home/developer --shell /bin/bash developer This command will create a new user called whose home directory is at and default shell as Bash ( ). Note that this is an interactive command so you will need to supply a password and some other (trivial) information. developer /home/developer /bin/bash Then we will set its primary user group as . This is required for Nginx to be able to serve files. To do that, run: www-data usermod -g www-data developer To verify that you have successfully run these commands, do this: id developer Example of successful outcome: . Bracket value of uid shows the username and the bracket value of gid shows the name of the primary group. The first value should be and the second value should . uid=1001(developer) gid=33(www-data) groups=33(www-data) developer www-data To run elevated commands, you need to add your non-root user to the group to run elevated commands. DigitalOcean published an on how to do this but here is a quick summary. First, run the following code to add user to the group: sudo excellent article developer sudo usermod -aG sudo developer To verify that your non-root user was successfully added to the sudo group, run: . This command "switches" into sudo mode of that user instead of running a specific command via . To exit, simply type and you will be back to root user session. More information on this command is . su - developer sudo exit available here Troubleshooting tips for beginners If you made a typo, you can delete a user: deluser --remove-home username groupdel username Hint: the second command is required to remove the old primary group Confirm the user deletion: getent passwd username Confirm the primary group deletion: getent group username Hint: if the entry is deleted, will return nothing getent OpenSSH Once your droplet is up and running, it is time to configure SSH keys to allow password-less authentication. Ubuntu droplet image comes pre-installed with OpenSSH so there is no need to install it. Using SSH keys with a running ssh-agent allows you to cache passphrase so you need to enter it only once for each new Bash session. For security reasons, it is better to separate the account password from SSH key passphrase. Keys Now we will generate an SSH key pair (public and private) on your local machine and add it to your droplet to allow a password-less connection. This contains step-by-step explanations of how to do it. When following the tutorial from DigitalOcean, bear in mind that you need to set up SSH access for the user, not the user. In other words, when copying the public key to remote, replace destination with . tutorial from DigitalOcean developer root ~ /home/developer/.ssh Note that command is not suitable for setting up keys for users. As the syntax of the command is , the public key will be copied to the account . Switch is for specifying a non-default key, a different user. ssh-copy-id other ssh-copy-id … user@host user -i not You will also need to create an SSH entry with a custom hostname in your config. This is required for rsync Bash script which we will use to copy modified source code from local to remote. SSH config is typically located at: . ~/.ssh/config See and for more details. Bear in mind that the sample config below contains which is a . this tutorial from DigitalOcean OpenSSH man page AddKeysToAgent Mac-specific setting Sample Nginx config: Host dev HostName <ip> User <user> PreferredAuthentications publickey AddKeysToAgent yes UseKeychain yes IdentityFile ~/.ssh/<user> Before logging out, make sure your account has been set with a password. Root access can be very valuable if your stops functioning. For example, DigitalOcean provides . To set a password, simply execute: when logged in as root. Alternatively, you may need to . root OOBM console access passwd reset the root password Key points if you are a beginner By default, will generate keys in the current folder ssh-keygen Typically, you store SSH keys in so navigate to that directory first ~/.ssh/ You need to add a key to before attempting a connection private ssh-agent To start your ssh-agent, do: eval " " $(ssh-agent -s) To automatically start ssh-agent, add the line above to your profile script To add SSH key, run: ssh-add private_key You can automatically add SSH keys (see sample SSH config above) Config information for beginners The information below relates to droplet Quick file reference: Config files location: /etc/ssh/ Primary config file*: sshd_config Auto-load configs from: /sshd_config.d/ *Do not confuse with : sshd_config ssh_config is for connecting to this host ( ) sshd_config this droplet is for connecting to other hosts ssh_config from the droplet Disable your root SSH connection (explained below): PermitRootLogin no Initial root password . Accessing root over SSH is a dangerous security practice (details) At the beginning of this article, I have mentioned choosing password-based authentication instead of SSH key when creating a new droplet. I have done so with the following two assumptions in mind: separate article into clear sub-sections to help beginners this is an explorative article, not an authoritative guide Troubleshooting steps Run to check if your private SSH key was added or not: ssh-add -l Troubleshoot SSH connection locally: ssh -v user@host Troubleshoot SSH connection remotely: add (or uncomment) the following line in (droplet): sshd_config LogLevel DEBUG log path: /etc/var/auth.log you may need to restart ssh service: service ssh restart Make sure you set correct permissions: the directory (local machine) should be owned by the current user: chown -R $USER:$USER ~ /.ssh/ the directory (local machine) should have permissions 700: chmod 700 ~/.ssh the file (droplet) should have permissions 600: authorized_keys chmod 600 /home/user/.ssh/authorized_keys Node Node will be installed using . First, determine which version of Node you need to run. You can do so by running: . Then you can install Node by running: Snap snap info node snap install node --classic --channel <node_version> For example: snap install node --classic --channel 14/stable You can confirm the installed Node version by running: . If you installed the wrong Node version, first uninstall it by running: . Then proceed to install the desired version following previous instructions. node --version snap remove node Nginx Canonical published an excellent step-by-step on installing Nginx in Ubuntu. tutorial Canonical article tells you to create your website config in directory. Alternatively, you can create your site config in and symlink it to . This way you can easily enable/disable site config. When you create a symlink, you must use an absolute path: sites-enabled/ sites-available/ sites-enabled/ ln -s /etc/nginx/sites-available/site_config /etc/nginx/sites-enabled/ Canonical article tells you to restart nginx whenever you make config modification via . Alternatively, you may simply reload config instead which is considered a safer option ( ): service nginx restart more details here service nginx reload Sample Nginx config: server { listen 80; listen [::]:80; root /home/developer/repo/dist; location / { index index.html; } server_name my-domain.com www.my-domain.com; } #error_log /var/log/nginx/debug.log debug; If you do not have a registered domain, you can still use the domain name but with certain limitations. You need to modify your file to point your domain name to your droplet's IP address. For example: hosts 1.2.3.4 my-domain.com 1.2.3.4 www.my-domain.com The limit lies in the fact that others will not be able to use the same domain name unless they also modify their file. For the exact location of the file, please consult as its location varies per operating system. hosts hosts this document Do Not Use .dev TLD Use a .dev domain? Not anymore. Chrome & Firefox now force .dev domains to HTTPS via preloaded HSTS Chrome redirects .dev to https Quick information for beginners Quick commands reference: Restart: services nginx restart Reload: services nginx reload Test config: nginx -t Quick file reference: Config files location: /etc/nginx/ Primary config file: nginx.conf Auto-load configs from: conf.d/ Add website configs to: sites-available/ Auto-load websites from: sites-enabled/ Nginx and Permissions Web content directory ( ) needs to have: root directive owner: chown developer:www-data root_folder permissions: chmod 755 root_folder Otherwise, you will run into error 403 permission denied Troubleshooting steps for beginners If you run into various , enable debugging for more info HTTP errors Set (or uncomment) the following line in your site config: error_log /var/ /nginx/debug.log debug log If this logging level is too verbose, you can change it to other values When specifying a path for , you need to use an absolute path root directive UFW Any server, even a private one, should maintain a certain level of security. Having a firewall is one of those measurements. DigitalOcean image of Ubuntu has disabled by default. Before enabling UFW, you need to permit SSH (and other) ports otherwise your SSH connection will drop. To allow SSH connections, run: UFW ufw allow OpenSSH If you use non-standard SSH port (i.e. not 22), follow . Since we will also require Nginx connection, permit that as well by running: this tutorial from DigitalOcean ufw allow "Nginx Full" As you have noticed, if the app name has a space in it, you need to enclose it in quotes. To view a list of existing application profiles, run: ufw app list Note that these are pre-configured profiles that assume default ports. If you use non-standard ports, refer to the previously linked DigitalOcean tutorial. To enable UFW, run: ufw enable In case something goes wrong, you always have access via . OOBM console in DigitalOcean Troubleshooting tips for beginners To disable UFW: ufw disable Reset (delete all rules): ufw reset To delete a rule: ufw delete rule For example: ufw delete allow OpenSSH Generally speaking, whenever dealing with firewalls, routing tables, etc., it is . Ideally, you want in place in case your primary remote connection stops working. extremely important to verify twice or thrice that your changes will not break your current connection OOBM Rsync To synchronize source code from local to remote, a custom Bash script is used which uses and . To make this setup Windows-compatible, you would need to use (or ). rsync fswatch Cygwin similar The idea behind this script is simple. Watch the source code folder (typically repository) for changes. When a local change is detected, send modified files to remote. Download the script from . The repository is intended to be used as a . Given the long repository name, it is better to clone it into a folder such as . this repository git sub-module dev-server To start watching for file(s) changes, run: ./dev-server/watch.sh If you want to do one-off sync with the remote, run: ./sync.sh If you want to copy compiled files from a folder such as , run: dist/ scp dev:repo/dist/index.html . Or you can run the following to copy (recursively) the entire folder: scp -r dev:repo/dist/ . This Bash script makes several assumptions which are listed . Installation of development dependencies should be done directly on the droplet to avoid network overhead (use SSH). Make sure to set correct permissions for the web content folder as described in the . here Nginx section Troubleshooting tips for beginners If you run into permission errors, remove directory (droplet) using (or root account) and re-run sync script. /home/developer/repo sudo Conclusion In this article, I have provided a pragmatic approach towards moving source code compilation from your local machine to a remote one. This should not be confused with a continuous integration server as it is an entirely different concept. In part two I will be looking at automating the provisioning of the remote development server using DigitalOcean meta-data and cloud-config. This will allow the ephemeral treatment of the droplet instance. This way you can spin droplets up and down as needed as they will be configured automatically. You would be able to use more expensive droplets daily without incurring a full month's expense. As explained in the beginning, this is not an alternative suggestion to Docker but rather a way to replace your local development processing with remote. This article does not claim to be authoritative but rather aims to be explorative.