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 — CDN configuration

CDN illustration

Why bootdev CDN config

One of BootDev’s website backend feature is CDN auto-deploy. The idea is we deploy the right way of using Amazon Cloudfront (CDN) automatically. You don’t need to investigate and do lots of work in Drupal to make things done.

From our hard earned experience, Drupal CDN configuration summarize as 2

  1. File storage + CDN (Drupal AmazonS3 module + CNAME with Cloudfront)
  2. Directly CDN (Drupal CDN Module)

1. Drupal AmazonS3 module

This module is great. It helps to move Drupal file system on Cloud. The advantage is you don’t need big local storage. But, you will be headache of using this. Some examples are

  • Generating XMLsitemap, the cron job will hang as it is not using local storage
  • new image style, you will need to turn off AmazonS3 module to add new style
  • Features module operations take longer, especially with image field operations
  • Multi-upload / insert image to editor, as the style generation link will be shown at the first time (/system/styles/files/….) will be different when the same style shown second time (CDN url). If you are not notice about it, your image will keep generate and waste lots lots of CPU.
  • Nginx Support, you will need to add AmazonS3 Nginx support (try url /system/stles/files)

For the multi-upload problem, you will need to insert images when u “edit” it rather than create. But, it will complain by your content editor. Another solution is running a quick fix database script to change the url in text body after image generated.

For the others, share some code snap as below, you can put this in your Drupal settings.php or config in admin page. Of coz, you will need to set your AWS right way like setting right S3 bucket as the Cloudfront origin, map to Route53 CNAME of your domain and put it on Cloudfront CNAME. That’s why you need bootdev if you are not understand what I’m talking about.

$conf[‘aws_key’] = ‘your key’;
$conf[‘aws_secret’] = ‘your secret’;
$conf[‘amazons3_bucket’] = ‘your bucket for AmazonS3 module’;
$conf[‘amazons3_cache’] = ‘1’;
$conf[‘amazons3_cloudfront’] = ‘1’;
$conf[‘amazons3_cname’] = ‘1’;
$conf[‘amazons3_domains’] = ‘cdn1.xxx.com
your mutiple CNAME, to increase frontend performance.
You will need this patch https://www.drupal.org/node/2044307#comment-9384777 ‘;

For support nginx, so that image styles can be generated, you will need this

  # 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;

2. Drupal CDN module

OK, now images are served. But what about the css/js/other static resources ? You will need CDN module.

You will need to config AWS to pull your data which we are not going to explain here howto. After that, you can have your CDN module to alter your site url, so that for examples: .css .js .xx will alter to CDN url.

Here is some basic config which u can put into your Drupal settings.php

$conf[‘cdn_basic_mapping’] = ‘http://d2fqrtkrfnbzdb.cloudfront.net| .css .js
http://yourlocalsite.com|.ttf .woff .svg .eot’;
$conf[‘cdn_mode’] = ‘basic’;

//CDN conf (Improve frontend performance)
$conf[‘cdn_farfuture_status’] = ‘1’;

d2fqrtkrfnbzdb.cloudfront.net is your CDN url, you can have other CNAME. You will see i put yourlocalsite.com as a CDN, which is not a good way. Why we are doing this is, we want to keep the webfront files serve as local. So that it won’t have CORS problem. Otherwise your font won’t work. Another approach is uploading your font to S3 manually and then manually set S3 CORS header and then set it as ClountFront origin. But, it make things complex and the font will leave the git repo management, which harder to deploy. So, we suggest to serve it locally, if you do not a very very large site.

You can also add .png next to .css / .js to serve your logo/icons or other resources. And Far future conf help to expire your resource at browser later and suggested to turn on.

To support far future, you will need below in nginx config, so that your CDN can curl the far future path. Reference: https://www.drupal.org/node/2380397

### 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;

After you do all above, you can have CDN run smoothly and also cloud storage + CDN for Drupal 🙂 Or use bootdev, one click, all set.


Why cloudfront / CDN ?

  1. Faster your content to audience
  2. Lower your AWS network traffic cost
  3. Available world wide
  4. Lower your Server CPU/Memory as requests share to CDN

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.


如何設計一個技術性的演講 (Technical presentation)

在上一次寫好計劃書之後, 今次就來準備演講. 很多時候, 當你的計劃書被接納之後, 都需要去演講. 因為大老闆的時間很寶貴的, 沒有時間去細看你的計劃書.

其實, 這次我準備演講的目的, 不是真正需要一次演講. 而是我希望跟老闆傾談自己概念的時候, 可以更加流暢. 準備演講的過程, 可以幫人整理問題, 找尋可能受批評的地方.

作為創業者 / 高層 / 年輕的你, 常都需要去表現自己, 相信了解如何演講可以幫助你.


演講 四大法則 Presentation 4P

  1. Product / Background (你的產品 / 計劃背景)
  2. Problem (現存問題)
  3. Possibilities (可行的方案)
  4. Possible solution / Recommendation (你所建議的方案)

這四個法則大概是整個演講的流程, 當然還需要一些頭尾配合, 在最後, 我會介紹我自己篇排的流程.

如果你是在介紹產品, 那你的第一個 p 就是說 product. 你需要說明這個產品的相關背景, 例如為什麼會想制造這個產品.如你你介紹的不是產品, 那就說明你的計劃背景.

第二個 p (problem) 就是說明現存問題, 很多時都是出現問題, 人才會找解決方案. 但成功了的人 (老闆) 很少會自己發現問題的.所以就由我們這些進取的下一代來告知他們.

第三個 p (Possibilities) 是希望你介紹現存的一些可行方案去解決 第二個 p 說明的問題. 為什麼方案一比方案二好 ? 為什麼你的方案可取 ? 就在這裡開始發揮了.

如果你是介紹產品的話, 第四個 p (possible solution)就是你整個演講的中心. 你就是在這裡推銷自己的產品, 說明為什麼你的產品可以解決第二個 p 的問題, 而第三個 p 中介紹的方案不可行. 個人來說, 我會把最多時間花在這裡.


  1. Presentation 4 P 之前, 必需弄清演講對象 (Target audience). 在對象未明之前, 不要準備演講
  2. Presentation 4 P 之前, 必需弄清楚演講目的. 你是在找尋伙伴 / 投資者 / 買家 ?
  3. 說明事例時, 事不過三.
    記住人的記憶力有限, 舉出事例時, 四個太多, 二個太少. 三個就適中了.
  4. 進入正提前要先說明演講內容與次序 (scope), 並建議聽眾有需要的話可更改次序.
  5. 多用圖解, 避免一個 ppt 板面多於 3 + 1 ( 3 論點 + 1 題目)
  6. 最後需要作個總結 (conclusion)
  7. 記住 ppt 不是你的講稿, ppt 的目的只是用作表示圖解, 動畫, 數據等
  8. 需要預備一些敏感題目答案, 這些往往會是別人發問的東西
  9. 一個正式演講約十五分鐘 + 五分鐘發問 (Q & A)


  1. Purpose 演講目的
  2. Scope 流程
  3. Background 背景
  4. Problem 問題
  5. Problem Examples (User experience scene) 案例
  6. Existing solutions 現行解決方案
  7. My product – How it can solve the problem ? 產品介紹
  8. Brief on my product architecture 產品設計
  9. Cost 成本
  10. Conclusion 總結

淺談設計技術性文章 – Technical writing for engineers and scientists

最近, 在寫技術性文章 / 計劃書 / 產品介紹 / 報告 時發覺, 
GOOGLE 竟然找不到一個好方法.
之後就去請教一位英文教授, 在他那裹借來了一本書
<<Pocket Book of Technical Writing - for Engineers and Scientists 2nd>>.
清楚易明簡潔地令你明白如何開筆, 一份技術性文章應該如何設計和應用什麼表達方式.
相信作為工程師 / 科學人員, 每天醉心於研究的你們, 

從這本書了解到, 寫技術性文章約有四大部份

  1. 文章的目的和針對讀者
  2. 針對技術性文章的特點
    1. 技術性話題
    2. 多用圖形 / 圖畫表達
    3. 數字資料以表達數量與方向
    4. 由淺至深, 準確的表達方式
  3. 決定文章性質
    1. 計劃書
    2. 報告
    3. 研究報告
    4. 說明書
    5. 建議書
  4. 選擇不同部份的表達方式
    1. 說明名詞的含意 (classify terms)
    2. 結構性陳述 (Description of mechanism)
    3. 流程性陳述 (Description of Process)
    4. 圖表 (Visual)


<<Pocket Book of Technical Writing - for Engineers and Scientists 2nd>> 
中建議了另一種計劃書格式 (Informal Proposals)

Informal Proposals

  • Purpose - Describe the reason for writing this proposal (文章目的)
  • Background - Describe the problem that needs to be solved (問題背境)
  • Scope - Review what this proposal will and will not cover (文章範圍)
  • Approach - Describe the proposed solution to the problem (解決方案)
  • Result - Show how the solution will solve the problem(成效)
  • Statement of work - List the tasks that will be performed (步驟)
  • Personnel - List those who will be doing the work and their qualifications (人才)
  • Facilities/equipment - List the physical resources required to do the work (資源)
  • Fiscal - List the financial costs of implementing the proposed solution (成本)
  • Time - List the time required to implement the proposed solution (時間)
  • Summary - Highlight the benefits and risks of adopting the proposal (總結)
  • Contract - Provide a contact for more information (聯絡)


在落筆的時侯, 我們需要記住 – 文章是用來服侍讀者的 ! We are serving readers.

在表達方式方面, 第一部份introduction 需要針對目標讀者, 使用較有吸引力的字句. 並在 scope 中說明這份計劃書只包括他們有興趣的話題.

Approach 這部份是文章的中心, 建議在這裹運用一些技巧 (Description of mechanism / Process, part by part description). 而且需要加上圖表, 因為讀者都是懶惰的, 技術文章最大可能給讀者留意到的就是圖表.
在這裹可以用些圖片, 例如系統結構, 方便讀者明白.
到 statement of work 這部份時, 因為在顯示步驟, 請用 point form 表達.

Resource / Costs
在這些部份都是說明一些跟時間金錢有關的東西, 所以必要用圖表 / 計算表等東西來表達. 切記在金額上加上貸幣名 (HK$ / NT$)

這部份就是文章的概括, 有時一些比較忙的老闆可能就只看這一部份. 所以在這裹說話可以修飾得漂亮一點.
最後, 當然是在 contract 部份留下你的大名.

多用圖表如 計算表 / 流程表 / 設計圖 / 結構圖 在什麼時候都有利 !

vpn solution2 – windows openvpn client 免費的vpn 客戶端

<!– @page { size: 8.5in 11in; margin: 0.79in } P { margin-bottom: 0.08in } –>

上文提及過 openvpn server的制作方法, 這篇文章會介紹服務器端與客戶端配合的一個例子.

在客戶端方面, 用家大部份都會用 Windows + openvpn. Windows 下安裝 openvpn 十分簡單, 只需要以下步驟

  1. 到這裡下載 openvpn 圖形介面客戶端

  2. click install.exe 安裝

  3. C:\Program Files\OpenVPN\config 創建 / 編輯 ovpn 檔案 (yourservername.ovpn)

  4. .opvn示範檔案如下

    1. client

    2. dev tap

    3. proto udp

    4. # change this to your server’s address

    5. remote 123.123..123..123 1194

    6. resolv-retry infinite

    7. nobind

    8. persist-key

    9. persist-tun

    10. #tls-client

    11. ca keys/ca.crt

    12. cert keys/keithyau.crt

    13. key keys/keithyau.key

    14. #ensure that we are talking to a server

    15. ns-cert-type server

    16. #confirm we are talking to the correct server

    17. #tls-auth ta.key 1

    18. # Select a cryptographic cipher.

    19. # If the cipher option is used on the server

    20. # then you must also specify it her e.

    21. cipher AES-128-CBC

    22. # Enable compression on the VPN link.

    23. comp-lzo

    24. #fragment 1400

    25. # enable user/pass authentication

    26. # auth-user-pass

  5. 把鑰匙拷貝到 C:\Program Files\OpenVPN\config\keys , 以下是在服務器端上鑰匙的制法 (keithyau 換成你的使用者名字, 詳情參考 https://keithyau.wordpress.com/2009/02/07/vpn-solution-2-openvpn/)

    1. sudo su
      cd /etc/openvpn/examples/easy-rsa/2.0/
      source ./vars

      ./build-key-server server
      ./build-key keithyau

      cd keys
      openssl dhparam -out dh1024.pem 1024
      cd ..
      openvpn –genkey –secret ta.key #optional

  6. 在右下角 openvpn icon按連接


  7. 測試連線

令服務器能接受以上設定的請求, 相應需要以下的設定

    # Which local IP address should OpenVPN
    # listen on? (optional)
    port 1194

    proto udp

    dev tap0
    #direct these to your generated files
    ca /etc/openvpn/examples/easy-rsa/2.0/keys/ca.crt
    cert /etc/openvpn/examples/easy-rsa/2.0/keys/server.crt
    key /etc/openvpn/examples/easy-rsa/2.0/keys/server.key
    dh /etc/openvpn/examples/easy-rsa/2.0/keys/dh1024.pem
    ifconfig-pool-persist ipp.txt
    #需要 dhcp 服務器 的配合
    # 服務器上沒有 dhcp 服務器的請選這行
    # server-bridge

    keepalive 10 120
    cipher AES-128-CBC
    #Push routing configuration
    #push “route”

    #tls-auth ta.key 0

    #fragment 1400
    #limit the number of connections
    max-clients 5
    #some secuurity settings
    # do not use if running server on Windows
    user nobody
    group nogroup
    #log file settings
    status openvpn-status.log
    verb 3
    # authentication plugin
    #forces client to have a linux acount in order to connect (Not for Windows user)
    # plugin /usr/lib/openvpn/openvpn-auth-pam.so login

這裡有安裝 DHCP server 的方法

sudo apt-get install dhcp3-server

sudo vi /etc/default/dhcp3-server

更改 為 INTERFACES=”br0″ # br0 = 你的網卡名稱

sudo vi /etc/dhcp3/dhcpd.conf

把其中一個示範修改為 ( 是你打算指派的網絡)

subnet netmask {


option routers;


/etc/init.d/dhcpd restart

/etc/init.d/openvpn restart

這樣你的 Openvpn 就能成功在 linux windows 間建立起來了