How to deploy site to Netlify manually and How to automate site deployment using FTP on other hosting service provider?

Netlify hosting has excellent integration with Github, so if you own a static website like me then you can rely on Netlify automated infrastructure for automatic deployment as well as update of your website.

But there might be a situation where you need to manually control the deployment of your static website by uploading the files with some FTP tool. I have prepared this guide keeping in mind the following scenario -

  1. Deploying manually on Netlify- You are using Netlify as your preferred hosting service provider and you want to manually upload the files to the Netlify server
  2. Automating the deployment on other hosting services- You are not using Netlify but rather other hosting services where you want to automate the process of uploading file manually to the server.

I have personally used both the options(Netlify hosting, Siteground Hosting) for deploying my static website. If you are on Netlify then you have to manually upload the files using their UI feature because Netlify does not support FTP.

But if you are using any other hosting service provider like me then you can automate the deployment using Ansible. Well this guide will help you to explore both the options in a more detailed way so that you can reduce the time for manual upload and deployment.

Table of Content

  1. Upload your site files manually to Netlify
  2. Automate deployment of the static website using FTP and Ansible
  3. Conclusion

1. Upload your site files manually to Netlify

Before we jump into the steps on how to upload the files manually to deploy your site on Netlify, I am assuming you already have a static website setup and active account on Netlify. If you are new to static websites and Netlify setup then I would recommend first going through the article - Hosting a website on Netlify

Here are the steps you need to follow -

  1. Output Directory- Generate the output directory for your website onto your local machine. I am using GoHugo for building my website so the command for generating the output directory is -


    (Note - Refer to official documentation of other static site builder(Next.js, 11ty, Gatsby,NUXTJS,Jekyll) to generate the output folder)

    Here is how I generate my output folder with Hugo -

    hugo command to generate to the output directory

  2. Upload to Netlify manually- Goto your Netlify account and after the successful login you will find the upload option onto the dashboard -

    upload option on Netlify for uploading the output directory manually

  3. Drag and drop ouput directory- You can simply drag and drop the output directory onto Netlify deploy section. Keep an eye on the deployment logs for any kind of issues.

    Netlify Deploy logs after successful manual upload of output directory

  4. After successful upload and deployment you can verify your site by going back to Netlify dashboard and clicking on the autogenerated URL.

    Accessing the website after successful manual upload of output directory on netlify

1.2 Custom domain settings

It might be possible that after uploading the site manually on Netlify, your website themes are not loading properly, so check out this detailed guide on troubleshooting your theme loading issue

But here are the keys configs to look for -

  1. Check your baseUrl settings, the base URL should point to your custom domain.
  2. If you do not have a custom domain then keep the baseUrl pointing to the root directory .i.e. baseurl = "/"

2. Automate deployment of the static website using FTP and Ansible

(If you are planning to automate the deployment for your static website on Netlify hosting then please refer to Point-1 because Netlify does not support FTP.)

But if you own a static website and you not hosting your website other than Netlify then you will benefit from the following automation set up which I did for deploying my website using Ansible and FTP. Here is a little brief about my setup -

  1. Static Site generator - Hugo
  2. Website hosting service -
  3. cPanel and FTP(File transfer protocol) - Your hosting service provider must support FTP
  4. SSH key manager - For SSH connections using public and private key

Refer to the following steps-

  1. What we are trying to achieve - We will generate output directory of the static website and copy it to public_html on hosting service provider (

    Since I am using Hugo for building my static site, so I run hugo command to generate my output directory. Here is the screenshot of my output directory which I am planning to copy on my public_html directory of my Siteground account.

    ouput directory i need to copy to public_html directory of siteground

  2. Establish Authentication- Now we know what we need to copy from our local machine to the remote hosting server. But we need to authorize our local machine so that we can perform the copy operation.

    To establish authentication we need to generate SSH Public Key and Private Key.

    • Private key - It will be stored on your local machine along with the code of your static website and
    • Public key - It will be stored on the hosting server
  3. Generate Public Key and Private Key- Public key and private key cryptography in which we use one key for encryption and one key for decryption. We always keep private keep secrets and do not share them with others.

    Use the following command from your terminal to generate the public and private key pair -

     3Generating public/private RSA key pair.
     4Enter file in which to save the key (/home/vagrant/.ssh/id_rsa):
     5Enter passphrase (empty for no passphrase):
     6Enter same passphrase again:
     7Your identification has been saved in /home/vagrant/.ssh/id_rsa.
     8Your public key has been saved in /home/vagrant/.ssh/
     9The key fingerprint is:
    10SHA256:ytFPyPKRwiwZV10oj9sASc7aGo98ZXjq5eKRdn3zoXk vagrant@terraformvm
    11The key's randomart image is:
    12+---[RSA 2048]----+
    13|     ..... o.    |
    14|     oo.. o      |
    15|    . +. +       |
    16|     O +oo.      |
    17|    = O S+.      |
    18|   . B %.=.      |
    19|    + X + o o .  |
    20|     +.=   . =E. |
    21|     .o..   o..  |

    For security purposes, it asks for a password which you might want to assign for your public and private keys. It is better to assign some passwords for better security.

    Once you executed the above command then it should generate two keys id_rsa, inside the directory /home/vagrant/.ssh/. The directory name will be visible when you run the command ssh-keygen

    Here is my public and private key -

public and private key generation for authentication

  1. Upload Public key to Siteground/Hosting service provider- After generating the public key and private key now we will upload the public key to the hosting service provider.

    Since I am using Siteground so I will go to -

    Siteground dashboard -> Dev -> SSH Keys Manager

    If you are using any other hosting service provider then look for similar options, ideally, you should have similar options.

    SSH keymanager for uploading the public and private keys to siteground

    Goto->Import then copy and paste your public over here. It would be best if you opened the public key in some editor that you have generated in previous steps.

    Import public key to siteground

  2. Ansible script for automatic deployment- Now we are ready to write our ansible script for making the automated deployment of our static website on Siteground. Here is the screenshot of my ansible project structure and what it looks like. We will go through each of the files

    ansible playbook for uploading content to public_html

    jhooq-playbook.yml - This is the main playbook and here in my playbook I am using a remote user u116-sdev3 because Siteground hosting runs on Linux servers and this is the Linux user that they have provided me. So check with your hosting provider for the correct username.

    2- name: Ansible script to copy the files from local to public http
    3  hosts: all
    4  remote_user: u116-sdev3
    5  roles:
    6   - jhooq

    inventory/hosts - The next file we are talking about here is the hosts file. Here we need to specify the host name on which we will be uploading the public folder.

    So in my case, the hostname is and along with the hostname you need to provide the path of the private key. Please refer to the following code block -

    1[jhooq] ansible_ssh_private_key_file=inventory/jhooq-playbook/jhooq    ansible_port=18765

    I have renamed my private key from id_rsa to jhooq and copied the private key under inventory/jhooq-playbook/jhooq that is why i am using the path ansible_ssh_private_key_file=inventory/jhooq-playbook/jhooq

    ansible playbook for uploading content to public_html

    tasks/main.yml- This is the main ansible script where we will be generating the public output folder as well as uploading it to site ground.

    1. Run hugo command- As I am using Hugo for a static website, I need to write the ansible version of hugo command so that it can generate public output folder -

      1- name: Run the hugo command locally
      2  connection: local
      3  shell: "cd .. && hugo"
      4  register: localPath

      Public output directory

    2. Compress the output directory before uploading- The previous Hugo command will generate a public directory inside your project. Now we need to compress and prepare a zip file before uploading it siteground -

      1- name: Compress directory {{projectPath}}/public/ into {{projectPath}}//{{zipFileName}}
      2connection: local
      4  path: "{{projectPath}}/public/*"
      5  dest: "{{projectPath}}/{{zipFileName}}"
      6  format: zip 

      compress zip file before uploading it to the siteground

    3. Copy output directory to Siteground- Now we need to copy the zip file from the local machine to siteground server and here is the ansible task for that.

      1- name: Copy {{projectPath}}/{{zipFileName}} to
      2  ansible.builtin.copy:
      3    src: "{{projectPath}}/{{zipFileName}}"
      4    dest: "{{destinationDirectory}}"
      5    mode: 0770
    4. Extract the output zip file on the server- After successfully uploading the zip file to the server, now we need to extract the file, and this operation we are going to perform on a remote server. So here is the ansible task for that -

      1 - name: Extract {{destinationDirectory}}/{{zipFileName}} into {{destinationDirectory}}/public
      2   ansible.builtin.unarchive:
      3     src: "{{destinationDirectory}}/{{zipFileName}}"
      4     dest: "{{destinationDirectory}}"
      5     remote_src: "yes"
    5. Remove the zip file after the upload- At last remove the output zip file from the server so that it does not eat up your space on the server.

      1- name: Remove the {{destinationDirectory}}/{{zipFileName}}
      2  ansible.builtin.file:
      3    path: "{{destinationDirectory}}/{{zipFileName}}"
      4    state: absent

    vars/main.yml- There is one more file in which I have declared the variables which I have used throughout my playbook.

    2projectPath: "/Users/rahul/Documents/Jhooq-Project"
    3destinationDirectory: "/home/u116-sdev3/www/"
    4zipFileName: "" 
  3. Run the anisble playbook- This is my plabook run for automated deployment -

 1 $ ansible-playbook --inventory inventory/jhooq-playbook/hosts jhooq-playbook.yml
 3PLAY [Anisble script to copy the files from local to public http] *********************************************************************************************
 5TASK [Gathering Facts] ****************************************************************************************************************************************
 6[DEPRECATION WARNING]: Distribution fedora 31 on host should use /usr/bin/python3, but is using /usr/bin/python for backward compatibility
 7with prior Ansible releases. A future Ansible release will default to using the discovered platform python for this host. See
 8 for more information. This feature will be removed in version 2.12.
 9Deprecation warnings can be disabled by setting deprecation_warnings=False in ansible.cfg.
10ok: []
12TASK [jhooq : Run the Hugo to Generate HTML file] *************************************************************************************************************
13changed: []
15TASK [jhooq : debug] ******************************************************************************************************************************************
16ok: [] => {
17"msg": "/home/customer"
20TASK [jhooq : Run the hugo command locally] *******************************************************************************************************************
21changed: []
23TASK [jhooq : debug] ******************************************************************************************************************************************
24ok: [] => {
25"msg": "Start building sites … \nhugo v0.93.2+extended darwin/amd64 BuildDate=unknown\n\n                   |  EN  |  PT   \n-------------------+------+-------\n  Pages            |  218 |   10  \n  Paginator pages  |   40 |    0  \n  Non-page files   |    2 |    0  \n  Static files     | 3747 | 3747  \n  Processed images |    0 |    0  \n  Aliases          |   49 |    4  \n  Sitemaps         |    2 |    1  \n  Cleaned          |    0 |    0  \n\nTotal in 11361 ms"
28TASK [jhooq : Compress directory /Users/rwagh/Documents/Jhooq-Project/public/ into /Users/rwagh/Documents/Jhooq-Project//] ***************************
29changed: []
31TASK [jhooq : Copy /Users/rwagh/Documents/Jhooq-Project/ to] *******************************************************************************
32changed: []
34TASK [jhooq : Extract /home/u116-sdev3/www/ into /home/u116-sdev3/www/] **************
35changed: []
37TASK [jhooq : Remove the /home/u116-sdev3/www/] *************************************************************************
38changed: []
40PLAY RECAP ****************************************************************************************************************************************************         : ok=9    changed=6    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

3. Conclusion

This is how I automated my static website deployment on Netlify as well as on Siteground, if you are also using the static website for your blog/website you could the above instructions for automating the deployment so that you do not have to manually copy-paste the files.

If you find this setup cool and want a copy of my ansible project send me a mail from the contact page and I will make my ansible project public on GitHub repo so that it can benefit others also.

Read More -

  1. Why I dumped Wordpress and switched to Hugo static site generator
  2. My experience of hosting static website hosting on Siteground
  3. Hosting a website on Netlify
  4. How to set up Netlify CMS for static website?
  5. Fixing the Hugo Theme on Netlify Hosting?
  6. /netlify-deploy-site-with-ftp-manually?