Why Bootdev — Dynamic CDN

In the old days, we put images, css / js, woff etc any assets to CDN, so that clients can download somewhere that geographically optimised.

Around end of 2012 – early 2013, new idea comes out like we should CDN everything, so that we can reduce the complex architecture with memcache, page cache cluster (Varnish) or even microccache etc. Just 1 layer cache and having everything to CDN. Like architecture below.

dnamic cdn

Your website domain name will directly point to CDN with CNAME. And then the CDN will point to your load balancer address or your web server. So it is like a proxy. When you do create, it will 100% bypass CDN and goes to web server, when u do UPDATE, the CDN will update from web server then invalidate itself, when you do DELETE, the CDN will bypass request to web server and invalidate its cache. When you read, it read from the CDN, not from web server. So the CRUD action can be completed.

You will need your own cache invalidation strategy, like send update to cloudfront / using versioning object or url.

Here is a sample conf of how we bypass some URLs to go to web server, and making Drupal works.

E1EB6A9C-17EC-446D-AD59-80B471A4F962 62367506-DDC3-4E5C-8F05-24E2D20DBBBB

With AWS cloudfront, you can bypass header ORIGIN, so that you can preform CORs actions. Also you can use similar header bypass feature to detect mobile/PC. With such architecture well setup, theoretically, you can have unlimited PV, as your server wont be really hitted. Your bound will be write DB bound only, which is not a concern in most case.

If you don’t want to understand all these, but want to lower your cost and have higher traffic and faster response, contact bootdev at founders@bootdev.com ! We can deploy Dynamic CDN to you in minutes no matter you are using AWS or not. We can point our CloudFront account to your server, it can be Azure, Linode, or any bare-meter. It just need to be Drupal, and you can enjoy the best performance ever.

ref: https://media.amazonwebservices.com/blog/cloudfront_dynamic_web_sites_full_1.jpg

why BootDev — AWS management (EC2 decrease performance by time)

In our experience, if your EC2 got resource left for a period of time, lets say 1-2 months, even AWS say it is not, but we always experience performance drop. For example: server response time drop from 400ms to 600ms . Or CPU raised from 40% to 70% in average.

In many case, many users will not 100% * 24 * 7  consume their server resources. So, in my experience, AWS want to reduce cost and reallocate some resource privately to other clients. If your server is actually low utilization, it is OK. But, in my situation, we need high response time and we reserve the CPU for that. We will feel performance drop.

How we deal with that ? That is what auto-scaling group should do. If in your auto-scaling group, your server will keep renew itself. Like turning off old servers and start new servers, the EC2 resource allocation will maintain at best performance.

Such issue especially obvious in Drupal. Drupal PHP request involve in many hooks / functions / third party calls etc. Server CPU requirement is higher than other web PHP applications. So, renewing server can help to keep better performance.

How to deploy auto-scaling group ? Call BootDev 🙂

Hope above information can help anyone that feel AWS sometimes drop performance by time 🙂





Why BootDev — Nginx Config

We had spent many effort on Nginx configuration. Different with Apache + mod_php5, Nginx + php_fpm need much detail configuration and nginx is Drupal module dependent. It means some Drupal module require support from Nginx configuration. Like CDN module / Advagg module.

There is a github about Drupal + Nginx, but that will be too much and you will require to filter the necessary part in your project.

Here i share the main nginx configure

server {
  server_name *.compute.amazonaws.com;
  root   /opt/source/app;
  access_log  /var/log/nginx/access.log;
  error_log  /var/log/nginx/error.log;

  #include /etc/nginx/apps/drupal/drupal.conf;
  #Cache everything by default
  set $no_cache 0;
  #Don't cache POST requests
  if ($request_method = POST)
    set $no_cache 1;

  #Don't cache if the URL contains a query string
  if ($query_string != "")
    set $no_cache 1;

  #Don't cache the following URLs
  if ($request_uri ~* "/(administrator/|login.php)")
    set $no_cache 1;

  #Don't cache if there is a cookie called PHPSESSID
  if ($http_cookie = "PHPSESSID")
    set $no_cache 1;

  # Enable compression, this will help if you have for instance advagg module
  # by serving Gzip versions of the files.
  gzip_static on;

  location = /favicon.ico {
    log_not_found off;
    access_log off;

  location = /robots.txt {
    allow all;
    log_not_found off;
    access_log off;

  # This matters if you use drush
  location = /backup {
    deny all;

  # Very rarely should these ever be accessed outside of your lan
  location ~* \.(txt|log)$ {
    deny all;

  location ~ \..*/.*\.php$ {
    return 403;

  location / {
    # This is cool because no php is touched for static content
    try_files $uri @rewrite;

  location @rewrite {
    # Some modules enforce no slash (/) at the end of the URL
    # Else this rewrite block wouldn't be needed (GlobalRedirect)
    rewrite ^/(.*)$ /index.php?q=$1;

  location ~ \.php$ {
    fastcgi_split_path_info ^(.+\.php)(/.+)$;
    #NOTE: You should have "cgi.fix_pathinfo = 0;" in php.ini
    include fastcgi_params;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    fastcgi_intercept_errors on;
    fastcgi_pass unix:/var/run/php-fpm-www.sock;
    fastcgi_read_timeout 40;
    fastcgi_cache MYAPP;
    fastcgi_cache_valid 200 301 30s;
    fastcgi_cache_bypass $no_cache;
    fastcgi_no_cache $no_cache;

    # Set cache key to include identifying components
    fastcgi_cache_valid 302     1m;
    fastcgi_cache_valid 404     1s;
    fastcgi_cache_min_uses 1;
    fastcgi_cache_use_stale error timeout invalid_header updating http_500;
    fastcgi_ignore_headers Cache-Control Expires;
    fastcgi_pass_header Set-Cookie;
    fastcgi_pass_header Cookie;

    ## Add a cache miss/hit status header.
    add_header X-Micro-Cache $upstream_cache_status;

    ## To avoid any interaction with the cache control headers we expire
    ## everything on this location immediately.
    expires epoch;

    ## Cache locking mechanism for protecting the backend of too many
    ## simultaneous requests.
    fastcgi_cache_lock on;

  # Catch image styles for D7.
  location ~ ^/sites/.*/files/ {
    try_files $uri @rewrite;

  # Catch image styles for AmazonS3 D7.
  location ~ ^/system/files/styles/ {
    try_files $uri @rewrite;

  location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg)$ {
    expires max;
    log_not_found off;

  location ~* \.(eot|ttf|woff|svg) {
    add_header Access-Control-Allow-Origin *;
    try_files $uri @rewrite;

  # Advanced Aggregation module CSS
  # http://drupal.org/project/advagg.
  location ^~ /sites/default/files/advagg_css/ {
    expires max;
    add_header ETag '';
    add_header Last-Modified 'Wed, 20 Jan 1988 04:20:42 GMT';
    add_header Accept-Ranges '';
    add_header Access-Control-Allow-Origin *;
    location ~* /sites/default/files/advagg_css/css__[[:alnum:]-_]+\.css$ {
      access_log off;
      try_files $uri @drupal;

  ### CDN Far Future expiration support.
  location ^~ /cdn/farfuture/ {
    tcp_nodelay   off;
    access_log    off;
    log_not_found off;
    etag          off;
    gzip_http_version 1.0;
    if_modified_since exact;
    location ~* ^/cdn/farfuture/.+\.(?:css|js|jpe?g|gif|png|ico|bmp|svg|swf|pdf|docx?|xlsx?|pptx?|tiff?|txt|rtf|class|otf|ttf|woff|eot|less)$ {
      expires max;
      add_header X-Header "CDN Far Future Generator 1.0";
      add_header Cache-Control "no-transform, public";
      add_header Last-Modified "Wed, 20 Jan 1988 04:20:42 GMT";
      rewrite ^/cdn/farfuture/[^/]+/[^/]+/(.+)$ /$1 break;
      try_files $uri @nobots;
    location ~* ^/cdn/farfuture/ {
      expires epoch;
      add_header X-Header "CDN Far Future Generator 1.1";
      add_header Cache-Control "private, must-revalidate, proxy-revalidate";
      rewrite ^/cdn/farfuture/[^/]+/[^/]+/(.+)$ /$1 break;
      try_files $uri @nobots;
    try_files $uri @nobots;


The idea of this config file is to support CDN far future, CDN, advagg, Drupal image styling, AmazonS3 and microcache modules. You need to catch different url pattern for different purpose.

For microcache, we put the cache into memory and expire every 30s, so that in each 30s, only the 1st visitor hit your site will generate by PHP. 2nd – N users will hit microcahe. In this approach, we can support high traffic website and then same time avoid handling cache invalidation problem.

Here I also share the cache config which we put nginx cache into memory which release better performance.

fastcgi_cache_path /dev/shm/microcache levels=1:2 keys_zone=MYAPP:5M max_size=256M inactive=2h;
fastcgi_cache_key "$scheme$request_method$host$request_uri";
add_header X-Cache $upstream_cache_status;
map $http_cookie $cache_uid {
  default nil; # hommage to Lisp :)
  ~SESS[[:alnum:]]+=(?<session_id>[[:alnum:]]+) $session_id;
map $request_method $no_cache {
  default 1;
  HEAD 0;
  GET 0;

You can read the comment inside the config file for more detail explanation.

This config requires to work with another PHP-FPM config, so that the memory is optimized. Then, you can estimate how many request per second that your server can serve. And i will talk about it next time.

Why bootdev — Caching (2)

Last time we talked about the 3 level of cache in Drupal + AWS.

This time we go down to more details of APC (Alternative PHP Cache). There are kinds of code level cache (OPcache) for php. Most common are OPCache (default PHP5.5), Xcache, APC. Mostly Drupal sites use APC.

There is also a Drupal APC module , in most case, that is not necessary to be installed. Unless you want to share some Drupal Cache table into APC. For bootdev, we don’t do this. Cache tables should be in a standalone cache instead of the web server, so that, while scale up servers, metadata can share across servers. So your system is ready for horizontal scaling.

You can simply install APC with pecl, the package manager of PHP

pecl install apc

Here I share the APC configure for EC2 m3.large machine which is 7.5GB memory

; configuration for php apc module
;Relative to the number of cached files (you may need to watch your stats for a day or two to find out a good number)

;Relative to the size of site

;The number of seconds a cache entry is allowed to idle in a slot before APC dumps the cache

;Setting this to 0 will give you the best performance, as APC will
;not have to check the IO for changes. However, you must clear 
;the APC cache to recompile already cached files. If you are still
;developing, updating your site daily in WP-ADMIN, and running W3TC
;set this to 1

;This MUST be 0, WP can have errors otherwise! Drupal can set to 1

;Only set to 1 while debugging

;Allow 2 seconds after a file is created before it is cached to prevent users from seeing half-written/weird pages

;Leave at 2M or lower. Site does't have any file sizes close to 2M

apc.rfc1867_prefix =upload_

You can have apc.php inside somewhere of /usr/share/php-apc depends on which server you are using.

Copy this file to your web root and access it, you can see the performance after tunning.

Screen Shot 2014-12-26 at 11.31.32 AM

Why BootDev — For website builders’ Backend as a service (wBaaS)

Recently, we launched a project, bootdev which deploys a configured Drupal site (Drucloud) into a pre given architecture into users’ personal AWS account. We call it website backend as a service (wBaaS). With this approach, we can deliver our hard earned experience to other business owner or developers without REWORK of what we did.

What we deliver is just configuration and knowledge. After the deployment, we dont host it, users’ host their site in their own AWS. We also provide a technical foundation / playground for people to add features /  experience best practice(s).

Currently available with Drupal 7.

With just 1 click, you can have pre configured like:

  • Caching
    • Nginx micro-cache
    • Cache pre-warmer
    • PHP APC
    • Memcache (AWS ElastiCache)
    • MySQL Query Cache
  • Database
    • AWS RDS configuration for Drupal
    • Multi-AZ
    • Secure by under AWS VPC
  • Web server
    • Nginx Drupal configuration
      • advagg css / js compression support
      • Image style catching
      • Micro cache
      • AWS Cloudfront CDN support (far future expire)
      • Cache control headers
    • PHP-FPM
      • APC
      • Max client configurations under EC2 m3.large
  • SOLR Search
    • SOLR index cron job
    • Drupal SOLR integration
  • DevOps
    • Cloudformation
    • Chef
    • Auto-scaling
    • Git deploy with bitbucket private repo + deploy key
  • Maintenance
    • System maintenance cron job
    • xmlsitemap
    • social network stat
  • Email
    • SPAM email control
    • Mass mail support (AWS SES support)
  • MAP
    • Google Map integration
  • Social
    • Social network meta-tag
  • File handling
    • Drupal S3 integration
    • Push to CDN
    • Separate file handling with other Drupal services
  • CDN
    • Drupal CDN configuration
    • Suport CORs
  • Server architecture
    • 2* EC2 m3.large web server
    • 1* EC2 m1.small chef server
    • 1* Mutli-AZ m3.large RDS
    • 1* EC2 m3.large SOLR server
    • 2* ElastiCache node m1.medium
    • Cloudfront CDN
    • Amazon S3
    • Auto-scaling with ELB
    • 2* EC2 m1.small GlusterFS
    • Inside VPC

In coming topics, I will explain why you need each of those configurations to make your site awesome.