How to Properly Deploy Web Apps via SFTP with Git

Share this article

How to Properly Deploy Web Apps via SFTP with Git

This article was peer reviewed by Haydar KÜLEKCİ and Wern Ancheta. Thanks to all of SitePoint’s peer reviewers for making SitePoint content the best it can be!


Uploading files is an integral aspect of any deployment process, and the underlying implementation can vary depending on the type of your server.

You can easily upload your files to an SFTP server using an open source desktop client like Filezilla. Those who have used this are aware that this process is cumbersome and irritating as it doesn’t let us automate our deployment process, and we always need to upload the whole project, even if we have modified only a part of the files of our codebase.

Abstract image symbolizing upload - a cloud shape made from glowing teal pixels, and in front of it, or inside it, an arrow pointing upwards

The PHPSECLIB (PHP Secure Communications Library) package has an awesome API for routine SFTP tasks: it uses some optional PHP extensions if they’re available, and falls back on an internal PHP implementation otherwise. You don’t need any additional PHP extension to use this package, the default extensions that are packaged with PHP will do. In this article, we will first cover various features of PHPSECLIB – SFTP, including but not limited to uploading or deleting files. Then, we will take a look at how we can use Git in combination with this library to automate our SFTP deployment process.

PhpSecLib Installation

composer require phpseclib/phpseclib

This will install the most recent stable version of the library via Composer.

Authentication

By default, password authentication is used to connect to your SFTP server. A cryptographic key-pair is more secure because a private key takes the place of a password, which is generally much more difficult to brute-force. Using phpseclib, you can connect to your SFTP server with any of the following authentication methods:

  1. RSA key
  2. Password Protected RSA key
  3. Username and Password (Not recommended)

RSA Key

We will assume that you have a secure RSA key already generated. If you are not familiar with generating a secure RSA key pair, you can go through this article. For a video explanation, you can refer to Creating and Using SSH Keys from Servers For Hackers.

To log in to a remote server using RSA key authentication:

namespace App;

use phpseclib\Crypt\RSA;
use phpseclib\Net\SFTP;

$key = new RSA();
$key->loadKey(file_get_contents('privatekey'));

//Remote server's ip address or hostname
$sftp = new SFTP('192.168.0.1');

if (!$sftp->login('username', $key)) {
    exit('Login Failed');
}

Password Protected RSA Key

If your RSA keys are password protected, do not worry. PHPSECLIB takes care of this particular use case:

namespace App;

use phpseclib\Crypt\RSA;
use phpseclib\Net\SFTP;

$key = new RSA();
$key->setPassword('your-secure-password');
$key->loadKey(file_get_contents('privatekey'));

//Remote server's ip address or hostname
$sftp = new SFTP('192.168.0.1');

if (!$sftp->login('username', $key)) {
    exit('Login Failed');
}

Username and Password

Alternatively, to log in to your remote server using a username and password (we don’t recommend this practice):

namespace App;

use phpseclib\Net\SFTP;

//Remote server's ip address or hostname
$sftp = new SFTP('192.168.0.1');

if (!$sftp->login('username', 'password')) {
    exit('Login Failed');
}

For other options such as No Authentication or Multi-Factor authentication please refer to the documentation.

Uploading and Deleting Files

A large part of the deployment process includes uploading files to a server. Uploading files essentially means transferring the contents of a local file to a remote file. The example below creates an index.php file on the server with the contents This is a dummy file:

namespace App;

use phpseclib\Crypt\RSA;
use phpseclib\Net\SFTP;

$key = new RSA();
$key->loadKey(file_get_contents('privatekey'));

//Remote server's ip address or hostname
$sftp = new SFTP('192.168.0.1');

if (!$sftp->login('username', $key)) {
    exit('Login Failed');
}

$sftp->put('index.php', 'This is a dummy file');

By default, put does not read from the local filesystem. The contents are dumped straight into the remote file. In a real world scenario, you need to fetch the contents of local files and dump them into the remote file:

$contents = file_get_content('path/to/local/file');
$sftp->put('index.php', $contents);

During our deployment process, we need to add one more step of deleting files and directories which are no longer required by the application. You can easily delete a single file or recursively delete all the files and directories from a specific directory.

//Deleting a single file. This does not delete directories.
$sftp->delete('index.php');

//Deleting directories and files recursively
$sftp->delete('dir_name', true);

Automating Deployment with Git

Git is widely accepted as the industry standard of versioning tools. Using the power of Git, we can save some time and bandwidth by uploading only those files that changed since the last upload. Using the SFTP API of PHPSECLIB, we will try to deploy our files to our SFTP server.

I have divided the whole deployment process into a few steps below to better explain each. Once we go through all the steps, we will bring it all together.

Some Git basics

First, we need to understand some specific Git commands which will assist us in our deployment process. We will create a class which will help us execute Git related tasks. Git commands are executed from the command line – we will use the Process component for that. The component provides an abstraction layer for executing shell commands and takes care of the subtle differences between the different platforms. You can refer to its documentation for more information. For now, we will write code specific to fetching the files added, modified or deleted between two given commits.

<?php

namespace App;

use Symfony\Component\Process\Process;
use Symfony\Component\Process\Exception\ProcessFailedException;

class Git 
{
    public function diffFiles($start_commit, $end_commit)
        {
        //Get new and modified files
        $process = new Process("git diff --name-only --diff-filter=AM $start_commit $end_commit");

        $process->setTimeout(3600);
        $process->setWorkingDirectory('/path/to/local/repository');
        $process->run();

        if (!$process->isSuccessful()) {
            throw new ProcessFailedException($process);
        }

    //Extract all file paths from the process output
        $files["added"] = array_unique(
        array_merge($files["added"], 
        array_filter(explode("\n", $process->getOutput()), 'strlen'))
    );

    //Get deleted files
        $process = new Process("git diff-tree --diff-filter=D --name-only -t $start_commit $end_commit");

        $process->setTimeout(3600);
        $process->setWorkingDirectory('/path/to/local/repository');
        $process->run();

        if (!$process->isSuccessful()) {
            throw new ProcessFailedException($process);
        }

        $files["deleted"] = array_filter(explode("\n", $process->getOutput()), 'strlen');

        return $files;
    }
}

Get contents of a file at a specific commit

Git stores the historical data regarding a given repository in the hidden .git folder. To move forward with our deployment process, we need to fetch the contents of files recorded by Git in the specified commit. We will add another method to the Git class:

public function getContent($commit, $file)
{
    //Get contents of a file at a specific commit
    $process = new Process("git show \"$commit:$file\"");

    $process->setTimeout(3600);
    $process->setWorkingDirectory('path/to/repository');
    $process->run();

    if (!$process->isSuccessful()) {
        throw new ProcessFailedException($process);
    }

    return $process->getOutput();
}

Deploying files

Next, we need to write a piece of code which will take care of executing the deployment process. Let us first list the important steps we need to take care of:

  1. Fetch added, modified, and deleted files between the two commits
  2. Transfer new and modified files to SFTP server
  3. Delete the files which were removed between the two commits

Now that we are clear on what we need to achieve, let’s put that into code:

$files = $git->diffFiles($start_commit, $end_commit);

if(count($files['added']) > 0) {
    foreach($files['added'] as $file) {
        $content = $git->getContent($end_commit, $file);

        //Ensure a directory exists - if it doesn't create it recursively.
        if (!$sftp->file_exists(dirname($file))) {
            $sftp->mkdir(dirname($path), -1, true);
        }

        $sftp->put('/path/to/remote/file', $content);
    }
}

if(count($files['deleted']) > 0) {
    foreach($files['deleted'] as $file) {

        if ($sftp->file_exists($file)) {
            $sftp->delete($file, true);
        }

    }
}

The above code will transfer all the updated files and delete the files removed between the two specified commits. It also takes care of automatic directory creation. If the directories are not present, the above code will ensure that those directories are created in the process.

Executing remote commands

You can execute any shell commands on the remote server before or after your deployment is completed with the exec method.

$sftp->exec("your-remote-command-here");

Just like with SSH, a call to the exec method does not carry state forward to the next exec call. To execute multiple commands without losing state:

$sftp->exec(
    "php artisan up" . PHP_EOL
    "composer dump-autoload -o" . PHP_EOL
    "php artisan optimize"
);

The phpseclib\Net\SFTP class extends the phpseclib\Net\SSH class. You can utilize the full API of the SSH class to execute remote commands or gather output. For an in-depth implementation of SSH, see our previous tutorial.

Managing Permissions

Permissions are the basic defense system of a file. We need to assign some specific rights to users or groups of users for files on the server to prevent potential attacks.

Setting permissions on a single file returns the new file permissions on success or false on error:

$sftp->chmod(0777, 'path/to/remote/file');

Alternatively, you can set the permissions for all files in a directory recursively:

$sftp->chmod(0777, 'path/to/remote/file', true);

Below is the summary of the most important methods regarding file permissions in the SFTP class:

Method Use case
chgrp($filename, $gid, $recursive = false) Changes file or directory group.
chown($filename, $uid, $recursive = false) Changes file or directory owner.
truncate($filename, $new_size) Truncates a file to a given length.
touch($filename, $time = null, $atime = null) Sets access and modification time of file. If the file does not exist, it will be created.

Downloading files

Using PHPSECLIB, we can also download things like backup files or user uploaded media from a server:

//Gets the remote file's contents
$content = $sftp->get('path/to/remote/file');

//Downloads and saves to the local file
$sftp->get('path/to/remote/file', 'path/to/local/file');

If the file is not present locally, it will be created automatically for you.

Alternatives

  1. git-deploy-php – a simple PHP based tool which deploys your changed files to your SFTP servers. It will transfer files automatically for you for the specified commits.
  2. PHPloy – another PHP based deployment tool to transfer files to SFTP servers. It has more features like rollbacks, support for multiple servers and submodules, and more. The downside is too much involvement in manual configuration for triggering a deployment.
  3. Deploy-Tantra – automatically deploys your repositories to SFTP servers once you push to the specified branch. The upside is a more managed workflow. The downside is it being commercial.

Conclusion

In this article, we introduced a particular way to help automate the deployment process for SFTP servers, and we have covered the configuration options necessary to get started.

How do you deploy to your SFTP servers? Can you think of some advanced ways of deploying? What are they? Let us know in the comments!

Frequently Asked Questions (FAQs) on Deploying Web Apps via SFTP with Git

What is the difference between FTP and SFTP?

FTP (File Transfer Protocol) and SFTP (Secure File Transfer Protocol) are both protocols used for transferring files over the internet. The key difference between the two lies in their security features. FTP transfers data in plain text, making it susceptible to data breaches. On the other hand, SFTP encrypts the data during transfer, providing a secure way to send files. It uses SSH (Secure Shell) protocol for encryption, ensuring that your data is safe from unauthorized access.

How does Git integrate with SFTP for web app deployment?

Git, a version control system, can be integrated with SFTP for efficient and secure web app deployment. You can set up a Git repository on your local system and another on your server. When you make changes to your web app, you can push these changes to the Git repository on your server using SFTP. This ensures that your changes are securely transferred and your web app is updated.

What are the prerequisites for deploying web apps via SFTP with Git?

To deploy web apps via SFTP with Git, you need to have Git installed on both your local system and your server. You also need to have SSH access to your server. Additionally, you should have a basic understanding of Git commands and how to use the command line.

How can I troubleshoot issues when deploying web apps via SFTP with Git?

If you encounter issues when deploying your web app, start by checking the error message. This will often give you a clue about what went wrong. Common issues include incorrect file permissions, problems with the SSH connection, or errors in your Git commands. You can also check the logs on your server for more detailed information.

Can I automate the process of deploying web apps via SFTP with Git?

Yes, you can automate the deployment process using Git hooks. Git hooks are scripts that run automatically when certain events occur in your Git repository. For example, you can set up a post-receive hook on your server that automatically deploys your web app whenever you push changes to the repository.

How can I revert changes if something goes wrong during deployment?

Git provides a powerful feature called ‘revert’ that allows you to undo changes. If something goes wrong during deployment, you can use the ‘git revert’ command to undo the last commit. This creates a new commit that undoes the changes made in the previous commit, allowing you to easily roll back changes.

Is it possible to deploy to multiple servers at once using SFTP with Git?

Yes, it’s possible to deploy to multiple servers at once using SFTP with Git. You can do this by setting up multiple remote repositories for your Git repository. When you push changes, you can specify which remote repository to push to, allowing you to deploy to multiple servers simultaneously.

How secure is deploying web apps via SFTP with Git?

Deploying web apps via SFTP with Git is very secure. SFTP encrypts the data during transfer, protecting it from unauthorized access. Additionally, Git allows you to track changes and revert them if necessary, providing an additional layer of security.

Can I use SFTP with Git for large web apps?

Yes, you can use SFTP with Git for large web apps. Git is designed to handle large projects efficiently, and SFTP can transfer large amounts of data securely. However, keep in mind that the transfer speed will depend on your internet connection and the server’s capacity.

What are some best practices for deploying web apps via SFTP with Git?

Some best practices include keeping your Git repository clean and organized, using meaningful commit messages, and regularly pulling from the repository to keep your local copy up to date. Additionally, always test your changes locally before deploying them to the server.

Viraj KhatavkarViraj Khatavkar
View Author

Viraj Khatavkar is a software developer, writer, speaker, and entrepreneur from Mumbai, India. He likes to challenge a complex set of problems with PHP. He’s fond of servers, swims and sometimes blogs

automationBrunoScontinuous deploymentdeploymentgitOOPHPPHPphpseclibsftp
Share this article
Read Next
Get the freshest news and resources for developers, designers and digital creators in your inbox each week