DEV Community 👩‍💻👨‍💻

Cover image for How to setup a Remote Development Server
Nico Braun
Nico Braun

Posted on • Updated on

How to setup a Remote Development Server

Setting up a development environment on a remote server yields several benefits. For example, It is easier to work on projects from different locations, one can simply ssh into remote and have all the tooling at hand. Or, Low tier machines aren't that much of a problem as the remote server, and not the local machine, has to use its resources to do the computing.

Since this server is not meant to serve production applications, most connection to the outside world can be blocked. In fact, all incoming connections except for a couple whitelisted IPs should be dropped without any response. We want to allow only our own machine/s to be able to connect via ssh. Nothing else. This will reduce the risk of attacks drastically.

Table of Contents


In order to follow along, you need access to a remote server or have a VM installed your machine. The commands shown here are based on RHEL CentOS 8.

Super User

As good measure, create a user and add it to the wheel group. Use this user instead of root. The user can run commands that require root privileges with sudo.

SSH as root into the server and create the user. At this occasion upgrade the kernel, before exiting.

ssh root@remote_host
adduser username && passwd username
usermod -aG wheel username
dnf distro-sync -y
dnf upgrade -y
Enter fullscreen mode Exit fullscreen mode

Create and deploy SSH Key

Add a public ssh key to users authorized keys file. The easiest way to achieve to is to copy the public key from the local machine via ssh onto the remote host.
Make sure you are connecting with the created user and not with root.

cat ~/.ssh/ | ssh username@remote_host "mkdir -p ~/.ssh && \
     touch ~/.ssh/authorized_keys && \
     chmod -R go= ~/.ssh && \
     cat >> ~/.ssh/authorized_keys"
Enter fullscreen mode Exit fullscreen mode

You can create the key with a tool called ssh-keygen.

Configure SSH

Connect back to remote and edit the config file.

ssh username@remote_host
sudo vim /etc/ssh/sshd_config
Enter fullscreen mode Exit fullscreen mode

Add the following lines to the config file.

LoginGraceTime 1m
PermitRootLogin no
PubkeyAuthentication yes
PasswordAuthentication no
PermitEmptyPasswords no
IgnoreRhosts yes
Protocol 2
AllowUsers username
Enter fullscreen mode Exit fullscreen mode

Before disabling password authentication, test the public key authentication by restarting sshd, exiting the session and reconnecting.
If it is not asking for your password anymore but logs you in, the key is working.

Restart the service.

sudo systemctl restart sshd
Enter fullscreen mode Exit fullscreen mode

Configure the Firewall

The next part step is to configure the firewall. Firewalld is pre installed in CentOS.

Change the default zone to block so that all incoming traffic is blocked without any acknowledgment. Add the public IP of your local machine to the public resource group. You can add multiple sources to each group, so this is essentially your IP whitelist. Lastly remove the two services not being used and leave only SSH available.

sudo firewall-cmd --set-default-zone=block
sudo firewall-cmd --permanent --zone=public --add-source=my.public.ip.address
sudo firewall-cmd --permanent --zone=public --remove-service=cockpit --remove-service=dhcpv6-client
sudo firewall-cmd --reload
Enter fullscreen mode Exit fullscreen mode

Check the config.

sudo firewall-cmd --list-all
sudo firewall-cmd --zone=public --list-all
Enter fullscreen mode Exit fullscreen mode

Disable IPV6

Next, disable IPV6 all together as it's not needed and reduces the attack surface.

sudo vim /etc/sysctl.d/disableipv6.conf
Enter fullscreen mode Exit fullscreen mode

Place the following entry to disable IPv6 for all adapter

net.ipv6.conf.all.disable_ipv6 = 1
net.ipv6.conf.default.disable_ipv6 = 1
# A particular network card instead of default can be accessed
# for example for the imaginary card enp0s3
# net.ipv6.conf.enp0s3.disable_ipv6 = 1
Enter fullscreen mode Exit fullscreen mode

Restart the Service

sudo systemctl restart systemd-sysctl
Enter fullscreen mode Exit fullscreen mode

If the below command does not return anything, ipv6, is disabled.

ip a | grep inet6
Enter fullscreen mode Exit fullscreen mode

Setup Fail2Ban

Lastly, install Fail2ban. fail2ban scans log files (e.g. /var/log/apache/error_log) and bans IPs that show the malicious signs.

sudo dnf install epel-release
sudo dnf install fail2ban
sudo systemctl start fail2ban
sudo systemctl enable fail2ban
Enter fullscreen mode Exit fullscreen mode

Everything is blocked except for SSH. Add some configuration for this particular use case.

sudo vim /etc/fail2ban/fail.d/ssh.conf
Enter fullscreen mode Exit fullscreen mode

The following setting will ban any IP forever, after three failed login attempts withing two hours. At this point, We don't expect anyone to be able to connect other than us. Some redundancy cannot hurt though.

bantime = -1
findtime = 2h
maxretry = 3

enabled = true
Enter fullscreen mode Exit fullscreen mode


Now the development environment is secure. All connections to the outside world and are cut off and only the ssh connection to the local machine is open. How does the actually workflow look like now? There are essentially two major ways, old and new school.

Old School a.k.a. Vim me out, Bro

The first method is how it's been done for a long time. The primary work environment will be Vim which is installed on the remote host. Some people like to use tmux and or zsh alongside. It is actually a very powerful combination. If you master these tools, you won't have to miss any feature.


New School a.k.a. VS Code GUI

VS Code has awesome remote development support. You can install extensions on the remote host and use the GUI locally.

Read more in the official docs.

Port Forwarding / SSH Tunnel

Ok, you can write some code now, that's great but how can an application actually be viewed? The answer is simple, run the app on the remote server on some port. Then forward whatever is sent on this port to the local machine.

Let's say you just started a React dev server on the remote host. The React app is being served at localhost:8080. You can now connect with a new shell but this time, only to forward the ports.

ssh -L -O localhost:6000:localhost:8080 username@remote_host
Enter fullscreen mode Exit fullscreen mode

Navigate to localhost:6000 in the browser of your local machine and see the application.

VS Code has some built in functionality around SSH Tunneling as well. You can read more in the official docs.

Wrapping Up

We have learned how to secure a remote development server, taking care of various configurations such the firewall and ssh. We have also looked at possible workflows for our environment and at SSH Tunneling.

It was an exiting trip, I hope you enjoyed it as much as I did.

Thank you.

Top comments (5)

herawais profile image
Muhammad Awais

Thanks for this valuable information. <3

kimotu profile image
Kimotu Bates

"Disable IPv6"? OMG. It's 2020 and many people even don't get any public IPv4 any more. 🤦‍♂️

Sloan, the sloth mascot
Comment deleted
dse profile image

You mentioned "It is easier to work on projects from different locations" as one of the benefits of your solution. As you can never know, what kind of connection will be available in some location, I think it's better to enable IPv6 whenever possible.

Thread Thread
codingsafari profile image
Nico Braun Author

Thanks for the input, I will rework this and change it to ipv6 and also warn about dynamic IPs.

🌚 Browsing with dark mode makes you a better developer by a factor of exactly 40.

It's a scientific fact.