Develop Locally, Test Remotely with Bitnami LAMP and Git
Introduction
When developing applications, having consistent server environments is key. This means that individual developers working on the application should use the same development environment, and development, production and test environments should also be consistent with each other. This reduces friction, simplifies the work of DevOps teams and avoids the "it works on my machine" syndrome.
Bitnami stacks provide a solution to this problem. Bitnami's LAMP stack, for example, provides a complete, fully-integrated and ready to run LAMP development environment for multiple platforms. Since it's the same image, just packaged differently for each platform, developers can write code on their local systems using the tools they're most comfortable with, then transfer that code to a cloud server running the same image without ever encountering any missing libraries or incompatibilities. For a solo developer, this out-of-the-box consistency can often save hours of time and effort.
This article will demonstrate how a solo developer can use the Bitnami LAMP stack to develop an application locally and make it available for immediate testing by a client or end-user on a public cloud server. With a little help from Git (which comes pre-installed in the Bitnami LAMP stack), application changes made by the developer on his or her local machine can be immediately reflected in the public test version running in the cloud.
The approach outlined in this article is best suited for a solo developer whose goal is to develop an application locally but also be able to deploy it to a public cloud server so that end-users can view and test it. Although this article uses the Bitnami LAMP stack and a PHP application as an example, the approach outlined here will also work for other Bitnami stacks and programming languages.
Assumptions and prerequisites
This guide makes the following assumptions:
- You know the basics of using PHP, Apache and Composer.
- You have a Bitnami LAMP VirtualBox virtual machine with SSH enabled.
- You have deployed the Bitnami LAMP stack on a cloud server.
- You know the public IP address of the cloud server.
- You have an SSH key pair and you know the basics of using SSH.
- You are able to log in to both the Bitnami LAMP virtual machine and the Bitnami LAMP cloud server from your host system using SSH.
To generate a new SSH key pair, you can use PuTTYgen (Windows) or the ssh-keygen command (Linux and Mac OS X). Read instructions on how to use PuTTYgen and instructions on how to use ssh-keygen.
Step 1: Create a custom PHP application on the LAMP virtual machine
The first step is to create an application on your local development environment: the Bitnami LAMP virtual machine. In this example, we'll assume that you're creating a PHP version of the board game Twister and that you've decided to use Laravel as the PHP framework.
Begin by logging in to the virtual machine using SSH and creating the directory tree for your application. These steps assume that your application will live in the /opt/bitnami/apps/twister/ directory:
cd /opt/bitnami/apps sudo mkdir twister sudo chown -R bitnami.bitnami twister/ cd twister/ mkdir conf htdocs
Create a Laravel skeleton application using Composer (included with the Bitnami LAMP stack):
cd htdocs composer require laravel/installer vendor/bin/laravel/laravel new -f .
Set permissions for the writable storage area of the application:
sudo chown -R bitnami.daemon storage/ sudo chmod -R 775 storage/
Create and edit the /opt/bitnami/apps/twister/conf/httpd-prefix.conf file and add the lines below to it. Notice that the document root for the application in this and subsequent configuration files is set to point to the public/ sub-directory of the Laravel application.
Alias /twister/ "/opt/bitnami/apps/twister/htdocs/" Alias /twister "/opt/bitnami/apps/twister/htdocs/" Include "/opt/bitnami/apps/twister/conf/httpd-app.conf"
Create and edit the /opt/bitnami/apps/twister/conf/httpd-app.conf file and add the content below to it. This is the main configuration file for your application, so modify it further depending on your application's requirements.
<IfDefine USE_PHP_FPM> <Proxy "unix:/opt/bitnami/php/var/run/phpmyadmin.sock|fcgi://phpmyadmin-fpm" timeout=300> </Proxy> </IfDefine> <Directory /opt/bitnami/apps/twister/htdocs/public/> Options +FollowSymLinks AllowOverride None <IfVersion < 2.3 > Order allow,deny Allow from all </IfVersion> <IfVersion >= 2.3> Require all granted </IfVersion> </Directory>
TipIf your application uses .htaccess files, you should change the AllowOverride None option to AllowOverride All.
Once you have created the files and directories above, add the following line to the end of the main Apache configuration file at /opt/bitnami/apache2/conf/bitnami/bitnami-apps-prefix.conf, as shown below:
Include "/opt/bitnami/apps/twister/conf/httpd-prefix.conf"
Restart the Apache server:
sudo /opt/bitnami/ctlscript.sh restart apache
You should now be able to access the skeleton application's welcome page at http://VM-SERVER-IP/twister. Here's a screenshot of what you should see:

Step 2: Create a local Git repository for your application
The next step is to create a local Git repository for your application code and then commit the initial set of files to it by running the commands below. Replace the example name and email address shown below with your correct information.
git config user.name "John Doe"
git config user.name "user@example.com"
cd /opt/bitnami/apps/twister/htdocs/
git init .
git add .
git commit -a -m "Initial commit"
By default, your changes will be committed to the repository's master branch. However, you'd typically want to save new, untested changes in a different branch and merge them into the master branch only once you're confident that they work as expected. With this in mind, create a dev-master branch for work in progress:
git checkout -b dev-master
This dev-master branch is the branch that you'll deploy to the cloud server for you and other users to view and test new changes in the application.
Step 3: Configure Apache on the LAMP cloud server
Once you have your local development environment up and running, the next step is to configure the Bitnami LAMP stack running on the cloud server (where you and/or end-users will test the latest changes). If you wish, you can replicate the same directory structure there but for variety, let's assume that you want the application on the cloud server to be visible at the document root - that is, at http://CLOUD-SERVER-IP/ instead of http://CLOUD-SERVER-IP/twister.
To begin, log in to the cloud server and delete the files in the current Apache document root:
cd /opt/bitnami/apache2/htdocs/ rm -rf *
Change the Apache configuration so that the document root points to the public/ directory of the Laravel application by editing the /opt/bitnami/apache2/conf/bitnami/bitnami.conf file and replacing all instances of the path /opt/bitnami/apache2/htdocs to /opt/bitnami/apache2/htdocs/public/. Here's an example of what the end result should look like:
<VirtualHost _default_:80> DocumentRoot "/opt/bitnami/apache2/htdocs/public" <Directory "/opt/bitnami/apache2/htdocs/public"> Options Indexes FollowSymLinks AllowOverride All <IfVersion < 2.3 > Order allow,deny Allow from all </IfVersion> <IfVersion >= 2.3 > Require all granted </IfVersion> </Directory> # Error Documents ErrorDocument 503 /503.html # Bitnami applications installed with a prefix URL (default) Include "/opt/bitnami/apache2/conf/bitnami/bitnami-apps-prefix.conf" </VirtualHost> ... <VirtualHost _default_:443> DocumentRoot "/opt/bitnami/apache2/htdocs/public" SSLEngine on SSLCertificateFile "/opt/bitnami/apache2/conf/server.crt" SSLCertificateKeyFile "/opt/bitnami/apache2/conf/server.key" <Directory "/opt/bitnami/apache2/htdocs/public"> Options Indexes FollowSymLinks AllowOverride All <IfVersion < 2.3 > Order allow,deny Allow from all </IfVersion> <IfVersion >= 2.3 > Require all granted </IfVersion> </Directory> # Error Documents ErrorDocument 503 /503.html # Bitnami applications installed with a prefix URL (default) Include "/opt/bitnami/apache2/conf/bitnami/bitnami-apps-prefix.conf" </VirtualHost> ...
WarningThe /opt/bitnami/apache2/htdocs/public/ directory does not exist yet, but it will be created the first time you deploy the application to the cloud server.
As an optional step, depending on your security requirements, password-protect access to the application.
Restart Apache to have the configuration changes take effect.
WarningYou will see a warning that the public/ directory, which is referenced as the Web server's document root, does not exist. Ignore this warning for the moment, as this directory will be created when you deploy the application to the cloud server.
sudo /opt/bitnami/ctlscript.sh restart apache
Step 4: Create a bare Git repository and post-receive hook
The next step is to create a bare Git repository on the server, and a post-receive hook that will accept new commits and deploy to the server document root.
Create a bare Git repository:
cd /home/bitnami mkdir twister.git cd twister.git git init --bare
Create a local environment file at /home/bitnami/twister.env for the Laravel application and fill it with the following content. This file is Laravel-specific, so you can omit this step for non-Laravel applications:
APP_NAME=Laravel APP_ENV=local APP_KEY=base64:+4eyQtLSPQ5p/gjj+qEKJ7i+RIEcLEvv+lYIDcTUOPA= APP_DEBUG=true APP_URL=http://localhost LOG_CHANNEL=stack DB_CONNECTION=mysql DB_HOST=127.0.0.1 DB_PORT=3306 DB_DATABASE=laravel DB_USERNAME=root DB_PASSWORD= BROADCAST_DRIVER=log CACHE_DRIVER=file QUEUE_CONNECTION=sync SESSION_DRIVER=file SESSION_LIFETIME=120
Create a post-receive hook at /home/bitnami/twister.git/hooks/post-receive and fill it with the following code:
#!/bin/sh # check out dev-master branch GIT_WORK_TREE=/opt/bitnami/apache2/htdocs git checkout -f dev-master # custom steps for deployment # update this depending on your application needs cd /opt/bitnami/apache2/htdocs sudo chown -R bitnami.daemon storage sudo chmod -R 775 storage composer update cp /home/bitnami/twister.env .env
Every time you push a new commit to this repository, the post-receive hook will check out the latest version of the dev-master branch to the /opt/bitnami/apache2/htdocs directory. It will then perform any custom deployment steps required to get your application working.
In this case, the steps are similar to those performed when you initialized the Laravel application in your local development environment. Depending on your application's requirements, modify the steps shown above as needed.
Make the post-receive hook executable:
sudo chmod +x /home/bitnami/twister.git/hooks/post-receive
Step 5: Configure SSH and test the Git remote repository
The next step is to configure SSH so that it's possible to log in to the Bitnami LAMP cloud server from the Bitnami LAMP virtual machine. This is needed so that you can push code changes from the virtual machine to the bare repository on the cloud server.
The easiest way to do this is by forwarding your SSH key on the host machine:
On your host system, add the following lines to the ~/.ssh/config file:
Host * ForwardAgent yes
Add your private key to the SSH agent on the host system:
ssh-add
You should now be able to log in to the Bitnami LAMP virtual machine over SSH and, from there, also log in to the Bitnami LAMP cloud server. In case of difficulties, read more about using SSH agent forwarding.
Next, configure the bare Git repository on the Bitnami LAMP cloud server as a remote repository and test it, as follows:
Log in to the Bitnami LAMP virtual machine. Add the cloud server as a remote server for your repository, as shown below. Replace the CLOUD-SERVER-IP placeholder with the public IP address of the cloud server:
git remote add test-server ssh://CLOUD-SERVER-IP/home/bitnami/twister.git
Push the dev-master branch to the cloud server:
git checkout -b dev-master git push test-server dev-master
This should trigger the post-receive hook script on the cloud server and cause the latest version of the dev-master branch to be deployed with all dependencies, permissions and configuration.
Browse to the cloud server's IP address and confirm that you see the application welcome page.
Step 6: Develop locally, deploy and test remotely
Verify that everything is working correctly by making a change to the application code in your local environment, pushing that change and viewing it on the cloud server.
In the Bitnami LAMP virtual machine, edit the welcome page view:
cd /opt/bitnami/apps/twister/htdocs vi resources/views/welcome.blade.php
Find the page title and change it. For example, change the default title "Laravel" to "Welcome to Twister!".
Save the changes, commit and push:
git commit -a -m "Changed title" git push test-server dev-master
Browse to the cloud server's IP address and confirm that you see the modified welcome page, as shown below:

You now have a basic system in place which will allow you to develop and commit changes locally on the Bitnami LAMP virtual machine, then immediately make those changes visible on a public Bitnami LAMP cloud server that you or your clients can access for testing or review purposes.
Useful links
To learn more about the topics discussed in this guide, consider visiting the following links:
- Bitnami LAMP stack documentation
- Steps to create a custom PHP application
- Password-protect access to an application with Apache
- FAQ for Bitnami virtual machines
The approach described in Steps 4 and 5 of this guide is based on information in the following tutorial:
In this tutorial
- Introduction
- Assumptions and prerequisites
- Step 1: Create a custom PHP application on the LAMP virtual machine
- Step 2: Create a local Git repository for your application
- Step 3: Configure Apache on the LAMP cloud server
- Step 4: Create a bare Git repository and post-receive hook
- Step 5: Configure SSH and test the Git remote repository
- Step 6: Develop locally, deploy and test remotely
- Useful links