Never Run Out of Space in SugarCRM or SuiteCRM using Block Storage on Digital Ocean

One issue that comes up occasionally for people hosting their own SugarCRM or SuiteCRM instance is running out of storage on their server. SugarCRM and SuiteCRM by default use a directory called ‘upload’ located in the root of your instance to store all file attachments. This tutorial will show you how to change this setting to another drive using Digital Ocean’s new block storage volumes.

Digital Ocean Droplet


Looking at Digital Ocean’s droplet pricing (as of October 2016) we see you get quite a bit of power with SSD speed in a $10-$40 per month VPS. For many applications, including SugarCRM and SuiteCRM, the stock storage of the VPS is sufficient. However, if your company works with large documents or uses an add-on from Sugar Outfitters to synchronize with a service like Box or Google Drive that storage can be used up quickly. While it is easy to upgrade to a higher sized droplet on Digital Ocean this can get pretty expensive quickly.


A new feature on Digital Ocean that solves this problem is called Block Storage where you can quickly add SSD storage to your VPS without having to upgrade to a higher sized droplet. This is a much less expensive option and allows you to quickly expand storage as requirements change. Below you can see the block storage pricing for Digital Ocean (as of October 2016). Let’s walk through the steps on how to change the storage location on your SugarCRM or SuiteCRM instance to a Digital Ocean Block Storage Volume.
Digital Ocean Block Storage Pricing


There are detailed instructions on how to install SugarCRM and SuiteCRM so this tutorial assumes you already have an up and running server with either SugarCRM or SuiteCRM installed. The instruction for adding block storage are identical for either platform.

Creating and attaching block storage


Log into your Digital Ocean account and click on the droplet you want to add storage to.

Click on Volumes from the options on the left and then click the ‘Add Volume’ button.

screen-shot-2016-10-16-at-10-19-06-pmNext you want to select the volume size you would like and name the volume. In this example we named this volume ‘crm-storage’. This is naming is completely up to you so use your own convention but remember to replace that name if you copy any commands below.


Creating the volume is quick and once complete you will see a popover listing the commands to mount the volume on your VPS. Below are the commands for this example. Note I have highlighted the volume name in red. I have also chosen to keep the same name on the server as the Digital Ocean interface. This is by no means required but may help if you have multiple volumes attached to one droplet. If you want a different name you would replace the highlighted red text after /mnt.

$ sudo mkfs.ext4 -F /dev/disk/by-id/scsi-0DO_Volume_crm-storage
$ sudo mkdir -p /mnt/crm-storage
$ sudo mount -o discard,defaults /dev/disk/by-id/scsi-0DO_Volume_crm-storage /mnt/crm-storage
$ echo /dev/disk/by-id/scsi-0DO_Volume_crm-storage /mnt/crm-storage ext4 defaults,nofail,discard 0 0 | sudo tee -a /etc/fstab

Now your volume is ready to use on your droplet. You can test that your new storage by logging into the droplet via SSH and creating a test file and reading it.

echo 'Hello, I exist' > /mnt/crm-storage/test.txt
$ cat /mtn/crm-storage/test.txt

In this example let’s create a directory for our production instance just in case you have a test environment set up that you would like to also store documents on the added volume. You also need to make sure the webserver user has write permissions to this directory just like you would if you were using the ‘upload’ of your CRM instance.

$ mkdir /mnt/crm-storage/production
$ sudo chown -R www-data:www-data /mnt/crm-storage/production
$ sudo chmod -R 775 /mnt/crm-storage/production

Using your favorite text editor edit the file named config_override.php located in the root directory of your instance. Add the following line and save the file. Just a reminder to change the names of the drive and or directory to follow what you have set up previously.

$sugar_config['upload_dir'] = '/mnt/crm-storage/production';


Now let’s create a Note with an attachment in the application to test. Log into your instance and create a Note with any attachment from your computer.


Create Note w/ Attachment


To see that the file was indeed stored on the new volume instead of the ‘uploads’ directory enter the following command into your terminal window.

$ ls /mtn/crm-storage/production

If everything was successful you should see something like ’26fde971-9465-2163-2c1d-5804565c552a’. This is normal as both SugarCRM and SuiteCRM store files as their unique id on disk and associates the filename in the database.

Lastly if you already have documents on your instance’s uploads directory those need to be moved to the new volume or you won’t be able access them.

$ cd <instance directory>/upload

$ mv * /mnt/crm-storage/production

I hope this tutorial is instructive and please add a comment if you have any questions corrections.  Also feel free to contact me directly if you prefer:

Shad Mickelberry



Denver – Montana

We got to Golden Gate State Park outside of Golden Colorado after a hard to day push from Durango. This was an awesome state park with tons of great trails. A client I’m working with is based out of Denver so I was able to meet with them in person which was great to put faces behind the voices.

Wyoming was relatively uneventful. We had a great time in Buffalo where we stayed at our first KOA on our trip. For a small town Buffalo had an incredible inter-urban trail system. One of the paths actually went all the way into the Bighorns. Everyone in Wyoming was extraordinarily nice in a surprisingly not annoying way.

Our next stop was Billings Montana. My father and step-mother met us there for a few days. This was a special visit because it was their first time meeting Tilly. Unfortunately Tilly was going through a bit of stranger danger and the first day or so she freaked out whenever my dad picked her up.

After Billings we headed to Helena Montana which was pretty high on our pre-trip list of places we wanted to look at to potentially live. After spending 2 weeks in Helena and seriously considered staying. We even looked at a house that was 2 blocks from trails and about 6 blocks from downtown. Despite the serial killer looking basement this 1872 house was beautiful and seemed to be in great condition. In the end we weren’t quite ready to end our trip and taking on another mortgage (we still own our house in Boulder City but are renting in out) would likely have added more stress than we wanted to take on.

Campsite at Devil’s Elbow Campground

All in all after 2+ months we are all doing well. We had our longest push to date leaving Great Falls making it all the way to Glendive, MT (about 350 miles). Instead of pushing hard we found a park part way through the drive and stopped at a park to relax and let the dogs out and Tilly got a chance to get out of her carseat. While a long day this stopped helped made the day much more bearable.



Trails around Helena
Taking a break with Tilly and Carl


After leaving Silverton we felt we were rushing the trip quite a bit. The longest we stayed in one place was 3 days. Pulling into Durango we were looking to stay 4-5 days at least for a little breather.

While it is cool having this chance to explore many different places one negative is we don’t know much about a place before we get there. We do some research but there is only so much you can do online.


We had booked 2 nights in an RV campground a few miles out of downtown Durango. The pictures looked lovely and they advertised Wi-Fi and laundry which we needed badly. Although the pictures online looked nice this RV park stunk. The sites are packed like sardines, the laundry was a rip off, and the “WiFi” was a big joke. That wouldn’t have been so bad but the campground was in a little canyon and I didn’t even have cell signal to use my hotspot.

Luckily we had only booked 2 nights and had a chance to explore the area and found a really awesome National Forest Campground at Junction Creek. We didn’t have any hookups but the fees were cheap and I was able to bike into town in the mornings to work.

View From Durango Library

We like it so much we ended up staying almost 2 weeks in Durango. This little break was nice and I was really productive since we weren’t traveling so much. I spent a lot of time at Durango Joes Coffee Shop and the Durango Public Library.

Hanging with family after swimming at rec. center pool

As we left Durango we reached a month on the road. Everything is going pretty well so far. We are finding our expenses are a bit higher than we expected. Camping, fuel, and groceries account for most of the discrepancy. This should level out this next month when we start receiving income from renting our house back in Nevada.



Ridgeway and Silverton

After leaving Grand Junction we headed South along Hwy. 550. We spent one night in Montrose, CO which was a really cool town. It would have been nice to spend more time there but we had already booked camping at Ridgeway State Park.IMG_1078

Old church converted to a pub in Montrose.

We spent 3 nights in Ridgeway State Park. The running was good and the park is really nice. I ended up dropping down the hill to the town of Ridgeway a few times to get work done when I needed connection.

IMG_1081View from campsite in Ridgeway. The park was awesome despite being close to the highway. Cimarron Mountains in the background.

By Thursday we were ready to move on. Our next destination was Silverton, CO. Several people had warned us of Red Mountain Pass between Ouray and Silverton. The warnings were definitely justified as there were several tense moments when there was oncoming traffic and pretty much no room on the side of the road and a pretty significant drop off. What was worse is the lane actually sloped sideways towards the cliff. Luckily the speed limit was between 10 and 25 MPH so taking it easy was no problem.

Silverton was a great time. We stayed at Molas Lake Campground which is about 10 minutes outside of Silverton at ~ 10,500′ elevation. The sun is pretty intense at this elevation and we didn’t have much shade at our campsite. We did some running on the Colorado Trail and I ran part way up Kendall Mountain from town. 


We ran into our first semi-major snag while in Silverton. We didn’t have service at the campsite and the air conditioner in our rental back in Las Vegas needed repairs. There was a heat wave and it hit 107 over the weekend. Luckily we had a management company that was able to get someone out for the repairs fairly quickly. The $1000 hurt a bit but it could have been much worse.

Another slight disaster we had was taking our dogs to Andrew’s Lake just South of Molas Lake. We took went out there on the recommendation of a local as a place we could let the dogs run around a bit. The lab was ‘extra’ excited about the lake for some reason when we arrived. We were walking around and playing fetch for a bit when we noticed he smelled really bad. It looked like he had got into something so we took him down to the lake to fetch sticks in the lake and wash off. After a few trips we noticed the smell was a bit more global than we thought and looking around we started seeing all these dead fish along the edge of the lake. Long story short we renamed Andrew’s Lake to ‘Dead Fish Lake’ and the 95lb lab wreaked of dead fish.

Trying to wash him in the trailer bath was quite and ordeal and all I accomplished was making the bathroom smell like dead fish. Both dogs slept in the back of the truck for a couple of days. When we got to Durango the first thing we did was look up a self serve doggy wash. Finally the dead fish smell is gone.


Grand Junction – One the Road Pt 2

If you didn’t see it here is part one Part One of the family ditching everything in Southern Nevada and deciding to hit the road. We left off as we left Crystal Geyser heading to Grand Junction…

So far in our adventure the longest we stayed in one location was two nights. We had covered a fair amount of ground considering towing a trailer, taking care of trailer utilities (dumping and filling), a seven month old baby, and two dogs is pretty slow going.

My wife Ashlee has friends in Grand Junction, CO so we planned on staying anywhere from 3-7 days there depending on how things were going. Our first stop was the North Fruita Desert Campground. On a map this didn’t look very far from Grand Junction but in practice it was about a 40 minute drive. Fruita itself isn’t far from Grand Junction but the the dirt road leading to the campground was 8 miles and the turnoff to the dirt road was 10 or so miles from the highway.

We ended up moving closer to town to a full on RV park which wasn’t our preferred type of place to stay but in the end was much more peaceful. Grand Junction ended up being a really good time. I was able to get quite a bit of work done and it was great hanging our with our friends who showed us around.

It so happened there was a low key race over the weekend called the Garfield Grumble. It is only about 4 miles but climbs and descends 2000 ft up and down the peak of Mt. Garfield near Palisade, CO. This was my first ‘race’ in over two years since my strange temporary paralysis (another story).
Mt. Garfield

We also met a bunch of people in town who were all super nice and inviting. This was particularly cool because like Las Vegas most people it seems in Grand Junction weren’t from there. Definitely recommend the brewery and distillery in Palisade about 15 minutes away from Grand Junction.

Trail in Grand Junction

All in all Grand Junction was a lot of fun. Our time there felt a bit hectic because there was a lot we wanted to do while we were there.

From here we are heading down to Ridgeway, CO for a few days.

Hitting The Road

So last week my wife and I sold all of our crap, rented our house, and set out on the road with our two dogs and seven-month old baby.

This wasn’t a spur of the moment decision. We have been thinking about living on the road for a couple of years now. Slowly we have sold/donated things we had accumulated individually and together.

We ended up renting our house in Boulder City, NV and packed our toy hauler and left town last Saturday (May 21st 2016). Our first stop was Sand Hallow State Park in Hurricane, UT.



The park is next to a reservoir and the area is a popular boating and OHV destination. After getting to the campsite and setting up we visited my friend Ron’s house in Springdale.

Before heading out the next day we relaxed a bit and each ran around the reservoir then sat by the beach with the baby.

Our next destination was Green Valley, UT but we learned our lesson about pushing it and driving too long on a recent trip to Carlsbad, CA. What is normally a 5 hour drive in decent conditions became a 7.5 hour drive between letting the dogs out and feeding/tending to the baby. We were all pretty fried after that and vowed to not make the mistake again.

So we stopped for the night at Freemont Indian State Park which is West of Richfield, UT on I-70. This is a beautiful location and it was a bit of a bummer we couldn’t stay but the location had no service so I couldn’t do any work.

We left early the next morning and decided to Boondock (free camping typically with no services) outside of Green Valley at a place called Crystal Geyser. Our original intention was to stay one night but this place was so relaxing we stayed two days.

The geyser would ‘erupt’ every 4-8 hours or so. Most were pretty unremarkable but one time the eruptions were around 15 feet high and this lasted for over 30 minutes. One interesting note about Crystal Geyser is that it is a cold water geyser fueled by CO2 underground.

Crystal Geyser UT

IMG_1061 IMG_1062


We left Crystal Geyser early on Wednesday May 25th for Grand Junction where we planned to spend several days to meet up with friends and check out the area as a possible place to live.

…. to be continued …

Easy PayPal Addon

This is just a quick and easy tip for adding payment options to your SugarCRM or SuiteCRM Invoices to make it easier to get paid and get paid faster.

PayPal has a nice feature called PayPal Me ( which provides a link you can share to take PayPal payments. After finishing the signup process you will get a unique url that is tied to your PayPal account:


One nice feature is you can append your url with an extra slash and enter the value of the payment so adds $100 to the form:

PayPal Me with Value


You can use this to easily add a link to your SugarCRM or SuiteCRM invoices.

  • SugarCRM 7
    • Go to Admin->PDF Manager->Invoice
    • Add the line
      • Pay with PayPal :<user_identifier>/{$|replace:’$’:”}
      •{$|replace:’$’:”} (example)
      • Notice the replace function for the PDF template. You need to remove the currency sign from the total value.
    • Use the editor to make the link a different color and clickable.
  • SuiteCRM
    • Modules->PDF Templates->Invoice
    • Add the line
      • Pay with Paypal<user_identifier>/$total_amount
      •$total_amount  (example)
    • Use the editor to make the link editable.

After that you will have a link your customer can use to pay you quickly. Remember as the person receiving money you pay the fees to PayPal. At the time of this writing those fees are 2.9% + $0.30 per transaction (


Sign up for my mailing list for updates

[mc4wp_form id=”916″]

PHP – Connect to remote SQL Server on Linux Server

If you are using PHP hopefully you are running on Linux which means you probably won’t have the unfortunate task of having to connect to MS SQL Server. Although rare, there may come a time where you need to integrate with an outside system that uses a SQL Server database and no other options for getting data are viable (ie APIs).

This post will outline how to set this up. In this case I was using Ubuntu 14.04 LTS 64 bit but the instructions should be pretty close to earlier versions or 32 bit. These are what worked for me on a clean Virtual Box installation then replicated on a remote server. It is possible some of the commands are redundant or not needed. Please comment if you find some. These instructions assume you already have the LAMP stack installed.

  1. Apt-Get Commands
    • sudo apt-get update
    • sudo apt-get install unixodbc tdsodbc php5-odbc freetds-dev unixodbc-dev freetds-bin
    • sudo apt-get update
    • sudo service apache2 restart
  2. /freetds/freetds.conf – Here you can declare multiple server parameters if you need to have multiple connections. In this case we are using the connection defined with ‘sqlsrv’
  3. /etc/odbcinst.ini
  4.  /etc/odbc.ini
  5. connect.php – PHP file to connect to database. This is just an example. You’d probably want to declare a database class and connect to the database as needed.

Parent Preview Dashlet for Activities

This post is about how to create a new dashlet for activity modules (Calls, Tasks, and Meetings) that will show a preview of the parent record for the activity in record view.

There are four files required for this customization. Three are related to the dashlet and one is a language file.

One important note for Sugar 7 dashlets is the directory and the .php, .js, and .hbs files must have the same name. So if you had a dashlet called hello-world you would need:

  • Directory hello-world
  • hello-world/hello-world.php
  • hello-world/hello-world.js
  • hello-world/hello-world.js

Since this dashlet will be used for multiple modules the dashlet files are located in ‘custom/clients/base/views/’.

The php file defines the dashlet filters. We want the dashlet to be available in the Calls, Tasks, and Meetings modules Record View so we add definition in the dashlet filter.


Next the javascript file checks if the activity record has a parent record associated with it. If it does we do an api call to get the object of that parent record.  It would probably be wise to add a check to see if the parent module type is one you are looking to show a preview of but I didn’t in this case because most users don’t have access to them (Cases for example). 


The handlebars template is where the dashlet view is set. In this instance Accounts and Opportunities as those are the primary modules used. This template shows some custom fields for this instance so you should remove those if you use this. I also already have a logic hook that displays the parent phone number on the call record. If that isn’t the case it would be very beneficial to add that to the template. Remember you’ll have to change depending on the parent module type.

Lastly we need to add the language file that will update the dashlet name in when going to add it along with the description.



Something I didn’t do but that would add some nice functionality would be to do another api call to try to get the notes associated with the parent module. I decided not to do this because the dashlet was intended to give a quick preview. I added the button to open the record at the bottom of the dashlet if the user needs more information. It would be interesting to see that similar to the preview from list view shows the related records in the intelligence pane.

Hope this slightly more advanced dashlet (compared to the proof of concept ones) helps someone if they want to create a dashlet like this or their own dashlet.

SugarCRM 7 Hide Completed Activities in Calendar

Early on when my company started with SugarCRM CE there was no option to hide completed activities in the Calendar so I followed this tutorial to change Calls/Meetings with status = Held and Tasks with status = Complete to background color = white. A later upgrade, I beleive 6.5.13, made this unnecessary with an added checkbox for hiding these items.

This worked well on our instance because we controlled the upgrades and I knew to update the files because the tutorial is not upgrade safe. We recently upgraded from SugarCRM CE 6.5.xx to Enterprise 7.6.  During our launch week I realized that the hide completed activities setting no longer existed. Additionally, we are on an On-Demand environment so the upgrades are controlled by SugarCRM so I wanted a different way.

Doing an inspect element in Chrome I noticed the div of an item, a Task in this case,  on the Calendar had this format:

 <div id="c51fbfab-5017-a40e-c307-55ef7dccc23b" class="act_item task_item" record="c51fbfab-5017-a40e-c307-55ef7dccc23b" module_name="Tasks" status="Not Started" detail="1" edit="1" duration_coef="1" style="border-color: rgb(1, 89, 0); height: 14px; top: -1px; left: -2px; width: 46%; background-color: rgb(177, 245, 174);"><div class="head"><div class="adicon" id="div_c51fbfab-5017-a40e-c307-55ef7dccc23b"></div><div>Expenses Update</div></div><div class="content" style="display: none;">Not Started</div><div class="content" style="display: none;"></div></div> 

Attempt 1 – Failed:

Noticing the ‘status’ attribute in the div this seemed like a pretty straight forward issue perhaps with some jQuery like:

 $('div[status="Held"]').css('display', 'none');

would do the trick but the activities weren’t affected. Just modifying a general <div> worked but not with referencing the status attribute.

Attempt 2 – Failed:

Next I tried adding some css by creating a file /custom/themes/custom.less with:

 div[status="Held"], div[status="Completed"] {
 display: none;

I saw the file in the browser editor with the syntax I wanted but still nothing was happening. I changed the file to:

 div {
 background-color: red;

This only changed the navigation bar and the footer to have background color of red. I don’t know why this was the case. Perhaps the order of page load?

Attempt 3 – Success: 

I knew the css worked as I had tested it in the editor. So I used a after_ui_footer global logic hook to include the css file when on the Calender page.


if (!defined('sugarEntry') || !sugarEntry) die('Not A Valid Entry Point');
class CalendarLogic
 function displayCalendarCss ($event, $arguments)
 {if (!isset($_REQUEST["to_pdf"]) && $_REQUEST['module'] == 'Calendar') {echo 'link rel="stylesheet" href="custom/modules/Calendar/Cal.css"/';}}

Please note the opending < and closing > for the link tags were removed in this post.



div[status="Held"], div[status="Completed"] {
 display: none;

This worked in testing but it took me a bit to get this packaged properly for testing but I finally got it.

Download the package below.

Hide Completed Activities Calendar Package

Perhaps a better way would be to override the calendar get activities to prevent the completed tasks from being included in the first place but I wasn’t having any luck overriding the base calendar functions.