Using hetzner to deploy application
Choosing OS
Normally a linux os and the most common is Ubuntu
Updating the OS and adding a user
after choosing the os, in ubuntu you can update the os by doing the following command:
sudo apt update # this will check for updates
apt list --upgradable # this will update the os
# when prompted for configuring openssh-server
# choose :
# Keep the local version currently installed
After updating you can now add the user using the following command:
adduser <username>
and fill up the necessary info like passwords
add the newly created user to the sudo group so that you can run sudo commands while still not having the same previlages with the root user
usermod -aG sudo <username>
You can now switch to the newly sudo
access user with
su - <usernamed>
then test it with
sudo ls /
Installing SSH
SSH is a protocol that allows you to securely log into a remote server.
-
after creating a user, create a ssh key so that we can disable the password login authentication
Instructions:
- run this command (need openssh to be installed)
ssh-keygen -t ed25519 -C "[email protected]"
- Enter the file path or pre “Enter for the default” (~/.ssh/id_ed25519 - this is from github)
- Set a passphrase for added security and configrm the passphrase
View the pub key
- Once completed, navigate to the .ssh directory in your user folder to locate the generated keys.
- Open the .pub file with a text editor and copy the content or run this command (dont forget that its .pub )
cat ~/.ssh/id_ed25519.pub | wl-copy
wl-copy for wayland, otherwise xclip
-
then paste the ssh key content in the ssh key content with your name on it
-
then deploy your ssh instance
Another Instruction using ecdsa
Hardening ssh
In order to avoid a bruteforce ssh attack, password must be disabled
-
(in local machine)
ssh-copy-id <user>@<vps-id-addr>
youll have to authenticate through this command and with this will enable a keybased authentication
then test it by login through ssh again and this time it should not ask for password again
custom ssh name
by default ssh will read
id_rsa
andid_ed25519
. if you have a custom name for the ssh key, you need to specify it in the command for example:ssh-copy-id -i ~/.ssh/<your_custom_ssh> <user>@<vps-id-addr>
<your_custom_ssh>
- is your PRIVATE key not public(.pub)or add a shortcut for ssh (see number 6)
-
editing the sshd_config
sudo vim /etc/ssh/ssh_config
here you can edit the following:
-
uncomment
PasswordAuthentication yes
and change it toPasswordAuthentication no
-
find
PermitRootLogin yes
toPermitRootLogin no
(remove the ability to login as root) -
find
UsePAM yes
toUsePAM no
NOTE
also check /etc/ssh/ssh_config.d/ for another file like
50-cloud-init.conf
if it exists also apply the necessary steps above to it (or remove it )
-
-
reload systemctl
to apply the changes reload the ssh service with:
sudo systemctl reload ssh
-
test to test you can logout the ssh and try logging in with root user
root@<vps-id-addr>
if you weren’t able to login meaning it is successful -
(optional) change the port to something else in the
/etc/ssh/ssh_config
Port
you can change the port to something else to avoid the attacks from automated scanners
you can do this by finding and uncommenting the
port 22
to likeport 8000
WARNING
make sure to take note of the changed port
-
(optional) Adding a shortcut to ssh
If you have a custom ssh name for your vps creating a shortcut will make sure that ssh will check that
private
ssh key instead of the default one (id_ed25519
)To create one do the following:
- cd to your
~/.ssh/
and create aconfig
- inside the config file is the following code:
Host <ssh-alias> #foo-server HostName <vps-id-addr> #192.168.123.456 User <vps-user> #bar IdentityFile <.ssh-private-key> #~/.ssh/id_foo IdentityFile <.ssh-private-key> #~/.ssh/id_foo_bar
<ssh-alias>
- the alias that you want to use when you run the ssh command (e.g.ssh foo-server
)<vps-id-addr>
- the ip address of your vps<vps-user>
- the user that you want to use to login to the vps<.ssh-private-key>
- the private key that you want to use to login to the vps
then you can login to the vps with the following command:
ssh <ssh-alias>
- cd to your
Pointing DNS
Add a dns record (cloudflare) and point it to your server ip address
ip addr
type: A name: @ points to: ip address ttl : 300
cloudflare and subdomain
if you’re managing your domain with cloudflare you can use the following command to point your subdomain to your vps ip address
Cloudflare dashboard → dns → records → add records
- type: A
- name: @ or
- IPv4 address: vps ip address
- Proxy status: Proxied(orange cloud)
- ttl : auto
TIP
install
tmux
so that when, for example, got disconnected you can just tmux attach
after pointing out it may take some time to propgate it.. you can check it if its already propagated with
nslookup <your website url>
once confirmed you can try to ssh using the website url instead in this format:
ssh <username>@<websiteurl>
# ssh [email protected]
Installing php and composer
When using ubuntu as the os first you need to add the repository for php:
(make sure that your os is updated)
sudo add-apt-repository ppa:ondrej/php
after that you can install php
sudo apt install php8.3-cli
Install the common php extensions
sudo apt install php8.3-common php8.3-curl php8.3-mbstring php8.3-xml php8.3-zip php8.3-gd php8.3-mysql php8.3-bcmath php8.3-sqlite3 php8.3-imagick -y
-y
is to automatically answer yes to all prompts
Installing composer
sudo apt install composer
then check if it is installed with composer -V
Installing mysql
use this command to install mysql in the server
sudo apt install mysql-server
mysql security steps
run this command for the setup
sudo mysql_secure_installation
-
Setting up validate password component This checks the strength of password
value:
Y|y
- then it will prompt for password level validation policy
value: 0 = low, 1 = med , 2= strong.
choose:
2
Strong password requires:numeric, mixed case, special characters and dictionary
- then it will prompt for password level validation policy
value: 0 = low, 1 = med , 2= strong.
choose:
-
Remove the anonymous users choose:
y|Y
when prompted -
Disallow remote root login choose:
y|Y
when prompted -
Remove the test database and access to it choose:
y|Y
when prompted -
Reload privilege tables now choose:
y|Y
when prompted
setting up mysql
login to mysql with the following command:
sudo mysql
this will open a mysql shell and you can run the following commands:
-
create the database:
CREATE DATABASE <database_name>; USE <database_name>; show databases; # will check all database
<database_name> - the name of the database you want to create. dont forget the
;
-
create the user
CREATE USER '<username>'@'localhost' IDENTIFIED BY '<password>';
<username>
- your username to be use to connect to the database you createdlocalhos
- will use the server’s local ip as the host<password>
- the password for your database user. password should follow the strong password requirements (mention in the mysql security steps) -
Grant previlages
GRANT ALL PRIVILEGES ON <database_name>.* TO '<username>'@'localhost'; FLUSH PRIVILEGES;
Grant previlages to the newly created user in order to have a permission to interact with the database(read write).
Then flush privileges afterwards to apply the changes after granting the previlages. This is force apply the privileges since by default mysql caches the privileges of the database
Simple setup for sqlite
When using sqlite as a database there are simple security measure that you can do.
- File permission and location:
# Set restrictive permissions
chmod 640 database.sqlite
# Only the web server user and owner should have access
chown www-data:www-data database.sqlite
Sqlite database should and must not in the /public directory. By default laravel is looking your /database.
- setup firewall to only access https,ssh
Setting up github
For security reasons, setting up github ssh keybased authentication is a must.
The process is almost the same with installing ssh but with a few differences.
-
generate a new ssh key
in your vps got to the
.ssh
directory and run the following command:ssh-keygen -t ed25519
name the ssh key for example
id_github
-
get a copy of the ssh key public key then check and copy for the content of the public key with the following command:
for wayland:
cat ~/.ssh/id_github.pub | wl-copy
for x11:
cat ~/.ssh/id_github.pub | xclip
if both ways are not working just cat the file and manually copy it.
-
Paste the key to github
In github account go to
settings -> ssh and gpg keys -> new ssh key
in the new ssh key section fill up the following:
- title: the name that you want
- key: the content of the copied public key from your vps
then save it.
you can checkout the github documentation for more information.
-
Save the ssh key to the ssh agent
start the ssh agent with the following command:
eval "$(ssh-agent -s)"
then add the key to the ssh agent with the following command:
ssh-add ~/.ssh/id_github #private path to the ssh key that you added with github
lastly, test the connection with the following command:
ssh -T [email protected]
if you receive a success message, you are good to go and can now continue installing the package and dependencies
Setting up nginx
for webserver and reverse proxy nginx is a common option although traefik is also popular.
to install:
sudo apt install nginx
check the version if its installed with nginx -v
then you can copy your vps id address and pase it in your browser
if it loads to nginx welcome page, meaning it is installed
Setting up node
you can use a version manager like nvm to easily manage your node and switch to the versions easily.
to install using curl:
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash
and run the following to your vps
export NVM_DIR="$([ -z "${XDG_CONFIG_HOME-}" ] && printf %s "${HOME}/.nvm" || printf %s "${XDG_CONFIG_HOME}/nvm")"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # This loads nvm
this will add node path in your vps but to persist it you can add it in your rc file.
- check for the current shell
echo $SHELL
normally it will be/bin/bash
. then you can add the code block above in the.bashrc
file.
install the lateast lts version of node using the following command:
nvm install --lts
Cloning and setting up the project
since we are using nginx as a reverse proxy, we need to clone the project in the default directory for the nginx
which is located in /var/www/html/
if you cd to the directory and run ls
you will see the following:
index.nginx-debian.html
this is the file that we saw when we accessed the nginx server through the vps ip address.
When cloning the project to this directory, you will encounter a permission denied error.
To fix this, you can take ownership of the directory and its contents with the following command:
sudo chown -R <username>:<username> /var/www/html
then you can clone the project with the following command:
git clone <github-project-url>
this will error because github ssh is not enabled in the vps. to set this up you can refer setting up github
installing the package and dependencies
after setting up github and cloning your project to html directory, you can run the following command to install the composer and its dependencies:
composer install
NOTE
if this fails. most likely, the extension is not installed. check the error and install the indicated extension
then try again.
-
Generate key When installing a laravel project, you will need to generate a key for the project.
Things to remember when generating a key:
-
.env
file should be in the root directory of the project and with the correct permissionYou can run
sudo chown <user> .env
Then you can run
php artisan key:generate
TIP
dont forget to modify the .env with the credentials needed for production
-
-
migrate the databases if the database is empty , especially if you are using sqlite, you might need to migrate your database
php artisan migrate # or php artisan migrate --seed
-
installing and building node dependencies
# pnpm is not resources hungry npm install or pnpm install
then run
npm run build
if encountered an error, you need to modify the tsc to be executable
chmod +x node_modules/.bin/tsc # if using vue chmod +x node_modules/.bin/vue-tsc
Depending on the project and the vps, installing node dependencies can be a bit resource hungry. For example, if you are using a vps with 1GB of ram, you can install the dependencies with
npm install
and it will take a while.Although there is a ci/cd technique in order to lessen the resource usage. check out [ci/cd technique using github action]
Changing project file permissions
In order for the web server to access the project files for reading and writing(like when user upload an image or a file), you need to change the file permissions.
Instructions:
-
go to the project directory (
cd /var/www/html/project-name
) -
then change the ownership of the project directory with the following command:
sudo chown -R $USER:www-data .
This command means :
- chown : change the owner of the file or directory
-R
: recursive- $USER : (value before the
:
) the user - www-data :(value after the
:
)the group of the user - . : the current directory
this is needed so that the user and the group www-data(nginx/apache) now have permission for the current project.
-
then change the permission for the files with the following command:
sudo find . -type f -exec chmod 664 {} \;
this command means:
-
find : search for files
-
. : the current directory
-
-type f : only search for files
-
-exec : execute the following command for each file found
-
chmod 664 : change the permission of the file to 644
- 6: read and write for owner(user)
- 6: read and write for group
- 4: read and write for others
-
{} : the file found
-
; : end of the command
NOTE
if you’re using sqlite, you probably need to change the permission of the file to 660 so that the web server can only write
-
-
also change the permission for the directories with the following command:
sudo find . -type d -exec chmod 755 {} \;
- -type d : only search for directories
-
give permission to the web server to access cache and storage
sudo chgrp -R www-data storage bootstrap/cache sudo chmod -R ug+rwx storage bootstrap/cache
-
chgrp : change the group of the file or directory
-
-R: recursive
-
www-data : the group of the user
-
storage and bootstrap/cache : the directory
-
chmod : change the permission of the file or directory
-
-R: recursive
-
ug+rwx : give permission to the user and group (ug) to read and write and execute
-
connecting project to server block
When using nginx the sites are located in the /etc/nginx/sites-available
directory.
If you install nginx and tried to open your vps ip in the browser the default
file in that directory is the welcome page site
So to add our site we have to create and open the file with the name of our website.
code instruction:
-
cd /etc/nginx/sites-available
-
sudo vim <website-name> ```
: the name of the website that matches your domain name or subdomain.
-
setup the server block
Go to laravel documentation and search for nginx server websites configuration.
alternatively, you can use this:
server { listen 80; listen [::]:80; server_name odio.tingexe.cc; root /home/forge/domain.com/public; add_header X-Frame-Options "SAMEORIGIN"; add_header X-Content-Type-Options "nosniff"; index index.php; charset utf-8; location / { try_files $uri $uri/ /index.php?$query_string; } location = /favicon.ico { access_log off; log_not_found off; } location = /robots.txt { access_log off; log_not_found off; } error_page 404 /index.php; location ~ \.php$ { fastcgi_pass unix:/var/run/php/php8.3-fpm.sock; fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name; include fastcgi_params; fastcgi_hide_header X-Powered-By; } location ~ /\.(?!well-known).* { deny all; } }
-
Then change and the
server_name
to your domain name or subdomain. -
and root to the project directory.(/var/www/html/project-name)
TIP
If you’re using your domain for the server_name also add the www version of the domain for better coverage (www.example.com)
-
-
create a symlink to sites-enabled directory
in order to activate the site you have to create a symlink to the sites-enabled directory
sudo ln -s /etc/nginx/sites-available/<website-name> /etc/nginx/sites-enabled
WARNING
make sure to use the full path of the directory
then confirm it by going to the sites-enabled directory and check if the file is there
cd /etc/nginx/sites-enabled ls -la
you should see the file with the name of your website and arrow pointing to the symlink which is in sites-available directory
-
test the nginx configuration
go to your project directory and run the following command:
sudo nginx -t
You should see the following message:
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok nginx: configuration file /etc/nginx/nginx.conf test is successful
-
restart nginx
sudo systemctl restart nginx
-
connect your project to your domain
After adding the domain to your vps and you try to connect to it, if it returns a bad gateway error(502).
Check your php extension
php8.3 -m
if you found nothing, you need to install the php fpm extensionsudo apt install php8.3-fpm
then restart the system ctl with :
sudo systemctl restart php8.3-fpm
Setting up ssl certificate
if your app is pointing to a cloudflare domain, most likely you’re using the proxy to have a ssl/tls with client and domain. However, to ensure that you have an end-to-end encryption, it is a good idea to setup an ssl from vps to domain(cloudflare)
the popular option is to use certbot:
sudo apt install certbot python3-certbot-nginx
-
Issue certificate for your website
go to your project directory and run:
sudo certbot --nginx -d <your-domain>.com
there will be a prompt to enter your email and questions like: Terms of service (choose
Y
) and offer to share you email with the Electronic Frontier Foundation (better choose :N
)
With that You can now have a working domain that is secured and in vps
Other known issues
if you try to git pull origin main
and get an error like a fatal error. This occurs when the ssh agent on your server stopped or expired at some point.
To resolve this, you can create a config file in your .ssh directory to avoid interruption for ssh agent.
cd ~/.ssh/
# create a config file
sudo vim config
# add the following content
User git
Hostname github.com
IdentityFile ~/.ssh/id_github
TCPKeepAlive yes
IdentitiesOnly yes
ServerAliveInterval 60
then change the ownership of the file
sudo chown <user> config
Sources and links
For the updated and detaild commands for vps setup check out this github