We have all used, and continue to use, Shared Hostings, it is something that will never change even if technology advances with servers in the cloud, and the reason is because they are: super cheap, I have even gotten some of $0.50 per month.
Many believe that to have automated deployment of your application on your server you need to have something in the cloud, in AWS, Azure or Google Cloud Platform. But this is not true, this can be automated even on servers as limited (so much so that some do not allow you to connect via SSH) such as Shared Hosting.
Even if your shared hosting does not allow you to connect via SSH and you need to execute some command line steps before uploading your application (for example to configure or install packages) you can do it with a PHP Shell, all such servers support PHP, so you can use that to execute commands in a shell even if the server doesn't support SSH connection.
What is CI / CD? 🤔
- Continuous Integration: It is the process in which each time you make a commit to your repository, it executes a series of steps to verify that your changes do not break existing functionalities, either by compiling the project (if it compiles, it works), and also by executing unit tests (if all the tests are met, everything is correct).
- Continuous Deployment: It is the process that comes after the Continuous Integration, although not immediately, it may be in specific versions of your application that this is executed; that is to say, that the application is not uploaded in each commit that we make, but in a new version, 1.1, which when it is created is because all the functionalities have already been tested and they comply with the requirements. This process describes uploading your app automatically to production without lifting a finger.
What do I need that for? 🙄
For two reasons: save time and avoid mistakes, we are all human, and humans make mistakes, machines don't. The machines only do one thing and they do it wonderfully, and that is to follow orders.
If you tell a machine to do the automatic deploy, it will do it. If you tell a human that he is tired of working 8 hours without a break, and maybe he went to bed late last night, that human might make a mistake in uploading some of the files manually. And this will make you lose money.
How to integrate that? 🧐
All shared hosting providers offer different functionality, some support Git, which you can use to upload your application, but others don't, so you just have to use the good old working one, FTP.
Why FTP? If you have integrated CI / CD in a VPS or a cloud server, you will surely do it in one of these ways:
- Place the link of your repository and the server does the work, in the case of serverless hosting.
- Configure the deploy by yourself using rsync over SSH or any other method that is not over FTP.
What we will do will be something similar to the second, only that this deploy will not be by rsync but by FTP.
But doesn't this make it less secure? What the developers default to is rsync, due to its security, file uploads are done via SSH protocol, which makes it super secure. But all shared hosting supports FTP over SSL, so it's still just as secure. Your data will not be exposed to any type of attack.
Step 1. Identify project 🛠️
Any type of project can be uploaded in this way, but it will depend on your server if it can execute it, for example you can upload a Ruby on Rails project with what I will show you today, but your server may not support Ruby so that you will not be able to run your application.
Laravel / Lumen
If your application is made in Laravel, you first need to do all the steps in this guide, and then make a symbolic link of your project's "public" folder to the httpdocs
or htdocs
folder, this can be done via SSH (if your server supports it), or via a PHP Shell.
In this guide at the end I will explain how to do this for applications in Laravel.
PHP
Depending on the file structure of your PHP project:
- If you have the entrypoint of the application in a "public" or "bootstrap" folder, you have to make a symbolic link to the
httpdocs
orhtdocs
folder. - If you have all your PHP files in the main directory you don't have to do anything else, just upload your folder with the files and that's it.
WordPress
In the event that you are developing a custom theme for your Wordpress, all you have to do is follow the steps in this guide and in the step of creating the FTP user you place the wp-content/themes/
directory of your server.
Step 2. Create FTP user
You can skip this step and use the default FTP user provided by the hosting, but I prefer to do this for two reasons:
- In case of password theft, this FTP user only has access to where my application's files are (not to the entire system), and if they are deleted, I simply have to change the passwords and run my Continuous Deployment again to have the application again online.
- I don't have to specify any absolute paths or any directories in my Continuous Deployment file, so if my code is open source users can't see where my application is hosted on my server.
So we create a user named git_ftp
or something like this, with a strong password, and we set it to only have access to the path we want our application to be on.
For a custom WordPress theme it should be the wp-content/themes
path.
For a PHP system it might be a subdirectory in the main folder, like httpdocs/php-app
, or the main folder itself, httpdocs
. The same goes for Laravel.
Step 3. Automating with GitHub Actions 💻
You can use any other CI/CD service, such as Azure Pipelines, or Travis CI, but to use GitHub Actions you don't need to create a separate account elsewhere, you just need your GitHub account, and nothing more. Even for private repositories (although it gives you 2,000 minutes per month, which is honestly more than enough for this).
The first thing you need to do is create a folder in your repository called .github
, and create another folder inside this called workflows
, and here create a file called continuous-deployment.yml
, and put this content in the:
name: Continuous Deployment
on:
push:
branches:
- master
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: 16
cache: "npm"
- name: Install Node.js dependencies
run: npm ci
- name: Build project
run: npm run build
- name: Deploy to the Server
uses: SamKirkland/FTP-Deploy-Action@4.0.0
with:
server: ${{ secrets.FTP_SERVER }}
username: ${{ secrets.FTP_USERNAME }}
password: ${{ secrets.FTP_PASSWORD }}
local-dir: ./
server-dir: ./
You can customize this file however you want, by default it has a step for the project to be compiled with Node.js, this is the case of projects with React.js, Laravel, etc. In case your project doesn't need that you can delete it.
You can also add steps to clear Cloudflare's cache or to do some extra command line steps that you do after each deploy, etc.
In addition to this, you can change the server-dir
field in the Deploy step, there they will place the path where the files will go on the server (e.g. wp-content/themes/
), and the local-dir
the directory to upload (e.g. the build
folder for React.js projects).
Now, if you realize there is no username or password here, how does it communicate with my server? We must define that through the GitHub Secrets in the repository.
Note: in case your repository is private and you don't have a paid GitHub account you must change those username
and password
fields with the respective values in the YAML file. But since it is your private repository, and only people from your team work there will be no risk of password theft, and if there is, that's why an isolated FTP user is created with only access to your application folder. Therefore, you can skip the next steps of adding GitHub Secrets.
Now, you have to go to your repository settings and go to "Secrets" > "Actions":
And then in the "New repository secret" button you add the following values one by one:
- FTP_SERVER: The IP or domain of your server to connect via FTP.
- FTP_USERNAME: The name of the FTP user that was created in the previous step.
- FTP_PASSWORD: The password of the FTP user that was created in the previous step.
And voila, with this you have integrated Continuous Deployment in your application. Just upload the changes and your application will be online without doing anything more than a simple push.
Step 4. Continuous Integration
This step should be essential, because sometimes by mistake of the developers by not testing their code before they upload the code and it goes directly to production without having executed the tests that prove that the code does not break any functionality. But not all projects have tests, either because they are too small to have them, or because programmers are too lazy to write them.
However, you can implement it by creating a file in the folder we saw earlier, .github/workflows
called continuous-integration.yml
, and placing this content in it:
name: Continuous Integration
on: [push, pull_request]
jobs:
format:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: 16
cache: "npm"
- name: Install Node.js dependencies
run: npm ci
- name: Run Prettier
run: prettier --write .
- name: Commit changes
uses: stefanzweifel/git-auto-commit-action@v4
with:
commit_message: "style: apply formatting changes"
branch: ${{ github.head_ref }}
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: 16
cache: "npm"
- name: Install Node.js dependencies
run: npm ci
- name: Run tests
run: npm run test
For now, what it is doing is that in each commit you make to your repository and each pull request it formats the code with Prettier although you can replace it with any other code formatter such as standardjs, or use a linter like ESLint.
In case your project is with PHP you must add some extra steps to this file in the format
job to run the tests with PHPUnit:
- name: Setup PHP
uses: actions/setup-php@v2
with:
php-version: "8.1"
- name: Run PHPUnit
run: composer run test
And that's it, you already have Continuous Integration implemented in your project, now each commit you make will be formatted to meet code style standards, and will be tested to verify that it doesn't break any functionality.
It even works with Pull Requests, so you don't have to test the pull request code on your PC, GitHub tells you if the tests failed or not.
Extra step. If your project is made with Laravel
Laravel runs in an entrypoint in the "public" folder that this is the folder that should go in your httpdocs
but this folder can't just be copied, it has to be a symbolic link, because this entrypoint is just the input to your application, all the code is in the other folders. That is why to run Laravel on a Shared Hosting and also integrate Continuous Deployment you must do a couple of other things:
Step 1. FTP User
Debes de crear una carpeta en el home del servidor llamada "repositories" y allí crear una carpeta con el nombre de tu proyecto.
El usuario FTP que creamos debes de actualizar la ruta a la que puede acceder por la ruta de esta carpeta.
Step 2. Execute Continuous Deployment pipeline
We must upload the changes via Git to our repository so that the CD is executed and our application is already uploaded in the "repositories/your-app" folder, with "your-app" for the name of your application.
Step 3. Run commands in a PHP shell or connect via SSH
If your server supports SSH simply connect to it and skip this below:
- Download the "shell.php" file from this repository, and upload it to your server.
- Go to the browser and navigate to your website and put the path "/shell.php" (e.g.
https://www.google.com/shell.php
).
Run the following commands:
$ ln -s ~/repositories/your-app/public ~/httpdocs/your-app
You have to replace the name of "your-app" with that of your application, and in case "httpdocs" is not the main public folder you must also change it. Also, in case you want to run your Laravel application when only its domain is opened (as the main application) you must delete the httpdocs folder and make a symlink to it just like in the command that it explains.
Conclusion
Although our server is a Shared Hosting, this does not prevent us from enjoying the automation and the things that the cloud already offers you by default, it simply takes a little more configuration but it will save you a lot of time and money later.
You can customize this to your liking, adding more steps to this Continuous Deployment, from sending the client an email telling them to check out the new version of the application, to compiling your project written in C++ and distributing the binaries in a Github Release. With code the limit is the sky! ✨.
Top comments (0)