Skip to main content

Setup a mirror for

I decided to host a public mirror for, you can find my mirror at
You can find out about becoming a mirror on there wiki and they have some setup instructions but was missing a few steps, so here's how i did it.
Note: This works for me on linux centos 5.x may not work the same on other systems.

IPv6 - First off you need to have a working IPv6 connection as multiple test are run against your IPv6 address.

DNS - You will need a domain name, or a sub-domain off of your existing domain name.
You need Full control of DNS for that domain/sub-domain and you will need to be able to create new DNS authoritative servers, specifically for the project, for one of the tests. If you use the same host, you need to set up this second instance of your DNS server in such a way that it gets unique configuration, unique IP address, and unique zone data, from your main DNS instance. (i used a separate server for this)

yuicompressor - yuicompressor is used to clean and minimize JavaScript and Cascading Style Sheets. The resulting output will have all comments removed, variable names shortened, and white-space removed as much as possible. This is to reduce the file size both for performance as well as bandwidth costs.
java is required by yuicompressor.
So go download a copy (current version 2.4.2) extract the files and copy the file "yuicompressor-2.4.2.jar" (found in the build folder) to "/usr/local/share/yuicompressor" you may need to to create the folder first. Then rename "yuicompressor-2.4.2.jar" to "yuicompressor.jar".

Tidy - Tidy is used to both validate and pretty-print HTML. Any parsing errors will produce noise on the screen and the resulting output will be properly normalized.
To install run "yum install tidy".

Apache and PHP - Apache "httpd". Any version is OK; but for best results, version 2.2.x is recommended. Modules needed: mod_headers, mod_expires, mod_env, mod_usertrack, mod_rewrite.
You will also need Apache's apxs tool. To install run "yum install httpd-devel".

PHP 5.2 or higher. As centos 5.x ships with PHP 5.1.6 you will need to get an updated package from a 3rd party repo, see adding repos on how to do this.
If you want the stats graphs to work you also need rrdtool. to install run "yum install rrd*"

Perl and perl modules - Perl is used for taking the fragments of HTML, JavaScript, and CSS, and combining them into their published forms.
The Template module specifically is called from perl to expand specific input files, do substitution (based on your configuration file), and produce the final versions of HTML, JavaScript, and CSS.
You will need to install this, i used cpan.
Other modules needed: YAML, YAML::Syck, and JSON. just run "yum install perl-YAML* perl-JSON".

Subversion - Subversion is used to get the files required. To install run "yum install subversion".

First thing to setup is DNS, you should know what site name you will want this test to go on. The primary site uses "". You might instead use "" (i used and that's what i will use through the rest of this setup).

DNS for this domain will need to have several records. The IP addresses you use will need to match those of the web server you plan on using, both for IPv4 and IPv6.

If using a sub-domain you will need to insure you delegate the zone correctly so people know where your zone records are. I added (see below) to my main domain.	IN	NS	IN	NS	IN	NS	IN	NS

Here's records, you will need to modify it for your DNS servers NS records, A records, AAAA records and domain name.

$ttl 15M  IN	SOA ( 
			1H )      IN NS      IN NS      IN NS      IN NS IN NS	        IN	A	        IN	A    	IN	A	        IN	A	        IN	A	        IN	A	        IN	A	        IN	A	IN	A      IN AAAA 2001:470:1f09:15a4::100b        IN AAAA 2001:470:1f09:15a4::100b      IN AAAA 2001:470:1f09:15a4::100b        IN AAAA 2001:470:1f09:15a4::100b      IN AAAA 2001:470:1f09:15a4::100b  IN AAAA 2001:470:1f09:15a4::100b     IN AAAA 2001:470:1f09:81e:21a:4dff:fe73:8a9b IN AAAA 5e4a:f49a:5e4a:f49a:5e4a:f49a:5e4a:f49a

Now to setup The IPv6-only DNS server. Operationally, this may be easier on separate hardware, or at least in separate virtual machines. (i did this on a separate server)
Here's records, you will need to modify it to your setup.

$ttl 38400
@	IN	SOA (
			38400 )
         IN NS
www IN A
www4 IN A
ipv4 IN A
ds IN A
a IN A
v4 IN A
beta IN A
archives IN A
 IN AAAA 2001:470:1f09:15a4::100b
www IN AAAA 2001:470:1f09:15a4::100b
ipv6 IN AAAA 2001:470:1f09:15a4::100b
ds IN AAAA 2001:470:1f09:15a4::100b
aaaa IN AAAA 2001:470:1f09:15a4::100b
v6 IN AAAA 2001:470:1f09:15a4::100b
beta IN AAAA 2001:470:1f09:15a4::100b
archives IN AAAA 2001:470:1f09:15a4::100b

and finally here's records, you will need to modify it to your setup.

$ttl 38400	IN	SOA (
			38400 ) IN NS IN AAAA 2001:470:1f09:81e:21a:4dff:fe73:8a9b

Now we need to create a new user, as i use virtualmin its easy i just add the domain i want and it creates the user and apache settings. The users home directory is "/home/".

Now login as the new user and create a new folder called "falling-sky-read-only"
then run

svn checkout falling-sky-read-only

this will download all the files needed.

Now to build the mod_ip module for apache.

cd /home/
su apxs -c -i -a mod_ip.c

After this is done, you will want to check your Apache configuration. Make sure that mod_ip is loading. After that, you can restart your Apache web server. Look for "LoadModule mod_ip_module" in your "httpd.conf".

Now to setup Apache's virtual host
The virtual host configuration can be a bit complex. We are using quite a few features to make this do everything wanted.
Note: This need to be the first virtual server for the IP address you are using (only effects you if you have more than one site on the ip address).
here's my virtual host for, you will need to modify it to your setup as a few parts are specific to my server, compare to the official docs.

<VirtualHost [2001:470:1f09:15a4::100b]:80>
SuexecUserGroup "#528" "#508"
DocumentRoot /home/
serveralias *
ErrorLog /var/log/virtualmin/test-ipv6.websters-computers.com_error_log
CustomLog /var/log/virtualmin/test-ipv6.websters-computers.com_access_log combined
ScriptAlias /cgi-bin/ /home/
ScriptAlias /awstats/ /home/
DirectoryIndex index.html index.html index.htm index.php index.php4 index.php5
<Directory "/home/">
Options +ExecCGI +FollowSymLinks +IncludesNOEXEC -Indexes +MultiViews
allow from all
AddHandler fcgid-script .php
AddHandler fcgid-script .php5
FCGIWrapper /home/ .php
FCGIWrapper /home/ .php5
AddDefaultCharset UTF-8
LanguagePriority en-us en
ForceLanguagePriority prefer fallback
AddLanguage de .de
AddLanguage el-GR .el-gr
AddLanguage en .en-us
AddLanguage en-PL .en-pl
AddLanguage en-us .en-us
AddLanguage fr .fr
AddLanguage fr-CA .fr-ca
AddLanguage hu-HU .hu-hu
AddLanguage it .it-IT
AddLanguage it-IT .it-IT
AddLanguage nl-NL .nl-nl
AddLanguage pt-BR .pt-br
AddLanguage sl-SI .sl-si
AddLanguage sl-SI .sl-si
AddLanguage sv .sv
AddLanguage zh-CN .zh-cn
AddLanguage zh-HK .zh-hk
AddLanguage zh-TW .zh-tw
# Enable etags.  Improve cachability of
# most stuff.
FileETag MTime
# Apache built in cookie tracking.
# We use this to record only the most recent submission from a given browser,
# to avoid stuffing the ballot box on the survey.
CookieExpires "24 hours"
CookieStyle RFC2109
CookieTracking on
# This virtualhost requires RewriteEngine
RewriteEngine on
# Cache Busting the javascript code
# Each time we update index.html, we do this:
#   <script type="text/javascript" src="/v71/index.js">
# This is to force the browser to fetch any updated javascript.
# We do a similiar thing for CSS.
# This RewriteRule will see /v71 and strip it from the request.
RewriteRule ^/v[0-9]+(/.*)$ $1 [N]
# Our script will generate precompressed versions
# of our HTML, JS, and CSS; and store the compressed versions
# on disk.  We want to serve these directly to users who support
# compression.  This avoids having Apache do compresion on-the-fly,
# lowering the load on the web server.
AddType text/html;charset=UTF-8 .htmlgz .html
AddType "text/javascript;charset=UTF-8" .jsgz .js
AddType text/css;charset=UTF-8 .cssgz .css
AddEncoding gzip .htmlgz
AddEncoding gzip .jsgz
AddEncoding gzip .cssgz
# If the browser supports gzip, redirect users
# of these file types to the compressed versions.
RewriteCond %{HTTP:Accept-Encoding} gzip
RewriteRule (.*)\.html$ $1\.htmlgz [L]
RewriteCond %{HTTP:Accept-Encoding} gzip
RewriteRule (.*)\.css$ $1\.cssgz [L]
RewriteCond %{HTTP:Accept-Encoding} gzip
RewriteRule (.*)\.js$ $1\.jsgz [L]
# mod_ip - reports IP address.  CGI arguements: callback= and fill=
# This is a custom module in the archive.
# This was implemented as a module for performance reasons.
# /ip/ is *the* single most requested element (6 or more times per test!)
# Comment out of mod_ip is not installed per the falling-sky
# install documentation.
<LocationMatch ^/ip/?$>
 SetHandler mod_ip
 Header append Cache-Control no-cache
 Header append Pragma no-cache
 Header append Expires "Thu, 01 Jan 1971 00:00:00 GMT"
	mod_ip_prefix 2001::/32 "Teredo"
	mod_ip_prefix 2002::/16 "6to4"
	mod_ip_prefix 2001:470::/32 " or"
	mod_ip_prefix 2001:55c::/32 "Comcast"
	mod_ip_prefix 2001:888::/32 "XS4ALL"
	mod_ip_prefix 2001:5c0::/32 "freenet6"
	mod_ip_prefix 2001:1291:200::/48 " ctbc"
	mod_ip_prefix 2001:1291:200::/40 " ctbc"
	mod_ip_prefix 2001:1418:100::/48 " itgate"
	mod_ip_prefix 2001:1418:100::/40 " itgate"
	mod_ip_prefix 2001:14b8:100::/48 " dna"
	mod_ip_prefix 2001:14b8:100::/40 " dna"
	mod_ip_prefix 2001:15c0:65ff::/48 " amis"
	mod_ip_prefix 2001:15c0:6600::/40 " amis"
	mod_ip_prefix 2001:15c0:6700::/40 " amis"
	mod_ip_prefix 2001:1620:f00::/48 " init7"
	mod_ip_prefix 2001:1620:f00::/40 " init7"
	mod_ip_prefix 2001:16d8:cc00::/40 " phonera"
	mod_ip_prefix 2001:16d8:dd00::/48 " phonera"
	mod_ip_prefix 2001:16d8:dd00::/40 " phonera"
	mod_ip_prefix 2001:16d8:ee00::/48 " phonera"
	mod_ip_prefix 2001:16d8:ee00::/40 " phonera"
	mod_ip_prefix 2001:16d8:ff00::/48 " phonera"
	mod_ip_prefix 2001:16d8:ff00::/40 " phonera"
	mod_ip_prefix 2001:1938:100::/40 " highwinds"
	mod_ip_prefix 2001:1938:200::/40 " highwinds"
	mod_ip_prefix 2001:1938:80::/48 " highwinds"
	mod_ip_prefix 2001:1938:81::/48 " highwinds"
	mod_ip_prefix 2001:1af8:fe00::/48 " leaseweb"
	mod_ip_prefix 2001:1af8:fe00::/40 " leaseweb"
	mod_ip_prefix 2001:1af8:ff00::/40 " leaseweb"
	mod_ip_prefix 2001:41e0:ff00::/48 " ipman"
	mod_ip_prefix 2001:41e0:ff00::/40 " ipman"
	mod_ip_prefix 2001:4428:200::/48 " acsdata"
	mod_ip_prefix 2001:4428:200::/40 " acsdata"
	mod_ip_prefix 2001:4830:1100::/48 " occaid"
	mod_ip_prefix 2001:4830:1100::/40 " occaid"
	mod_ip_prefix 2001:4830:1600::/48 " occaid"
	mod_ip_prefix 2001:4830:1600::/40 " occaid"
	mod_ip_prefix 2001:4978:100::/40 " yourorg"
	mod_ip_prefix 2001:4978:200::/40 " yourorg"
	mod_ip_prefix 2001:4978:300::/40 " yourorg"
	mod_ip_prefix 2001:4978:400::/40 " yourorg"
	mod_ip_prefix 2001:4978:f::/48 " yourorg"
	mod_ip_prefix 2001:4dd0:fc00::/40 " netcologne"
	mod_ip_prefix 2001:4dd0:fd00::/40 " netcologne"
	mod_ip_prefix 2001:4dd0:fe00::/40 " netcologne"
	mod_ip_prefix 2001:4dd0:ff00::/48 " netcologne"
	mod_ip_prefix 2001:4dd0:ff00::/40 " netcologne"
	mod_ip_prefix 2001:610:600::/48 " surfnet"
	mod_ip_prefix 2001:610:600::/40 " surfnet"
	mod_ip_prefix 2001:610:700::/40 " surfnet"
	mod_ip_prefix 2001:6a0:100::/40 " icm"
	mod_ip_prefix 2001:6a0:200::/48 " icm"
	mod_ip_prefix 2001:6a8:200::/48 " belnet"
	mod_ip_prefix 2001:6a8:200::/40 " belnet"
	mod_ip_prefix 2001:6f8:1000::/40 " easynet"
	mod_ip_prefix 2001:6f8:1100::/40 " easynet"
	mod_ip_prefix 2001:6f8:1200::/40 " easynet"
	mod_ip_prefix 2001:6f8:1300::/40 " easynet"
	mod_ip_prefix 2001:6f8:1400::/40 " easynet"
	mod_ip_prefix 2001:6f8:1c00::/48 " easynet"
	mod_ip_prefix 2001:6f8:1c00::/40 " easynet"
	mod_ip_prefix 2001:6f8:1d00::/40 " easynet"
	mod_ip_prefix 2001:6f8:202::/48 " easynet"
	mod_ip_prefix 2001:6f8:300::/40 " easynet"
	mod_ip_prefix 2001:6f8:900::/48 " easynet"
	mod_ip_prefix 2001:6f8:900::/40 " easynet"
	mod_ip_prefix 2001:770:100::/48 " heanet"
	mod_ip_prefix 2001:770:100::/40 " heanet"
	mod_ip_prefix 2001:7b8:1500::/40 " bit"
	mod_ip_prefix 2001:7b8:2ff::/48 " bit"
	mod_ip_prefix 2001:7b8:300::/40 " bit"
	mod_ip_prefix 2001:7e8:2200::/48 " ptlu"
	mod_ip_prefix 2001:7e8:2200::/40 " ptlu"
	mod_ip_prefix 2001:808:100::/48 " poznan"
	mod_ip_prefix 2001:808:100::/40 " poznan"
	mod_ip_prefix 2001:808:e100::/48 " poznan"
	mod_ip_prefix 2001:808:e100::/40 " poznan"
	mod_ip_prefix 2001:838:300::/48 " concepts"
	mod_ip_prefix 2001:838:300::/40 " concepts"
	mod_ip_prefix 2001:960:2::/48 " scarlet"
	mod_ip_prefix 2001:960:600::/40 " scarlet"
	mod_ip_prefix 2001:960:700::/40 " scarlet"
	mod_ip_prefix 2001:a60:f000::/48 " mnet"
	mod_ip_prefix 2001:a60:f000::/40 " mnet"
	mod_ip_prefix 2001:ad0:900::/48 " linxtelecom"
	mod_ip_prefix 2001:ad0:900::/40 " linxtelecom"
	mod_ip_prefix 2001:b18:2000::/48 " nfsi"
	mod_ip_prefix 2001:b18:4000::/40 " nfsi"
	mod_ip_prefix 2604:8800:100::/48 " cymru"
	mod_ip_prefix 2604:8800:100::/40 " cymru"
	mod_ip_prefix 2610:0100:4fff::/48 " gci"
	mod_ip_prefix 2610:0100:6000::/40 " gci"
	mod_ip_prefix 2a00:14f0:e000::/48 " gyron"
	mod_ip_prefix 2a00:14f0:e000::/40 " gyron"
	mod_ip_prefix 2a00:15b8:100::/48 " digiweb"
	mod_ip_prefix 2a00:15b8:100::/40 " digiweb"
	mod_ip_prefix 2a01:198:200::/48 " speedpartner"
	mod_ip_prefix 2a01:198:200::/40 " speedpartner"
	mod_ip_prefix 2a01:198:300::/40 " speedpartner"
	mod_ip_prefix 2a01:198:400::/40 " speedpartner"
	mod_ip_prefix 2a01:198:500::/40 " speedpartner"
	mod_ip_prefix 2a01:198:600::/40 " speedpartner"
	mod_ip_prefix 2a01:198:700::/40 " speedpartner"
	mod_ip_prefix 2a01:240:fe00::/48 " jaguar"
	mod_ip_prefix 2a01:240:fe00::/40 " jaguar"
	mod_ip_prefix 2a01:348:100::/40 " goscomb"
	mod_ip_prefix 2a01:348:200::/40 " goscomb"
	mod_ip_prefix 2a01:348:6::/48 " goscomb"
	mod_ip_prefix 2a01:8c00:ff00::/48 " ignum"
	mod_ip_prefix 2a01:8c00:ff00::/40 " ignum"
	mod_ip_prefix 2a02:2528:ff00::/48 " ipmax"
	mod_ip_prefix 2a02:2528:ff00::/40 " ipmax"
	mod_ip_prefix 2a02:278:1200::/48 " airwire"
	mod_ip_prefix 2a02:278:1200::/40 " airwire"
	mod_ip_prefix 2a02:980:1000::/48 " fullrate"
	mod_ip_prefix 2a02:980:1000::/40 " fullrate"
# Tell browsers that once /images/ [anything] is fetched, cache it forever.
# Don't even try and *ask* again.  These images do not change.  This will
# permit followup tests by the user both run faster, and lower the load on 
# the web server.
<Location /images>
 FileETag none
 ExpiresActive on
 ExpiresDefault "access plus 10 years"
<Directory /home/>
allow from all
RemoveHandler .php
RemoveHandler .php5
IPCCommTimeout 121
AuthName " statistics"
AuthType Basic
AuthUserFile /home/
require valid-user
DefaultLanguage en-us
AddDefaultCharset UTF-8

Once done save and restart apache.
you can test the virtual host by trying to bring up "" (change to your domain name) You should see something like this:


Now we need to copy some files to our web server folder, mine is located at "/home/"

mkdir /home/
mkdir /home/
mkdir /home/
mkdir /home/
cp /home/* /home/
cp /home/* /home/

Now we need to create a folder to temporarily store the files once built before i move them to the web server and we need to create our config file.
mkdir /home/
cd /home/

Now edit Here's what mine looks like. Change to your setup.

#! /bin/echo used by
# You can create "" to override anything you see here.
# For mirror sites, this might make tracking's code easier.
use strict;
$COMPRESS{"js"} =  "java -jar /usr/local/share/yuicompressor/yuicompressor.jar --type js --charset utf-8 -o [OUTPUT] [INPUT]";
$COMPRESS{"css"} = "java -jar /usr/local/share/yuicompressor/yuicompressor.jar --type css --charset utf-8  -o [OUTPUT] [INPUT]";
$COMPRESS{"html"} = "tidy -quiet -indent -asxhtml -utf8 -w 120 < [INPUT] > [OUTPUT]";
$INSTALL = "/home/"; 
$VARS->{"domain"} = "";
$VARS->{"ipv4"} = "";
$VARS->{"ipv6"} = "2001:470:1f09:15a4::100b";
$VARS->{"contact"} = 'Jason Fesler';
$VARS->{"mailto"} = '';
# Survey. if enabled, posts user results to survey.php for analysis.
# This is not critical to the user experienc.
$VARS->{use_survey} = 1;
# Replace IPv4, IPv6, and Cookies in the database with hashes.
# Caution: This will create a situation where post-analysis is
# greatly hampered, if you needed to to know what network
# the user was using.  This can affect re-scoring existing 
# database entries.
$VARS->{hash_survey} = 0;  
# If enabled, show the "stats" tab. 
# You are responsible for the stats page being generated based
# on your local stats.
$VARS->{use_stats} = 1;  # If enabled, shows the "stats" tab.  
# Like locales, allows selectively including different versions.
# You can have more than one value in this array.
# First match wins.
$VARS->{site_locals} = ["site"]; #In addition to .en-us, look for, .site 
# Look for buggy dns.
# Requires a DNS entry like this:
# buggydns1 AAAA  d8da.e472::0
# That's the IPv4 address converted to hex; in my case,
# Do not use this feature if your server IPv4 is between to
$VARS->{buggydns1_check} = 1;
# Look for proxy Via headers.
$VARS->{proxy_check} = 1;
#  # Stop running yuicompressor, harder to debug
#  # Warning: Much larger bandwidth requirements.
#  $COMPRESS{"js"} = "cat < [INPUT] > [OUTPUT]"; 
#  $COMPRESS{"css"} = "cat < [INPUT] > [OUTPUT]";

now as i want the stats to work i need to create a mysql database (if you don't want the stats set "$VARS->{use_survey}" to 0 and skip this bit.)
I have phpmyadmin setup so i just import "/home/" to my database.
next create th files to connect to the database.

touch /home/
touch /home/

now edit them and change to your settings. here's mine db.php

global $dbhandle;
  // Connecting, selecting database
  $dbhandle = mysql_connect("localhost",$USERNAME,$PASSWORD) 
      or die('Could not connect: ' . mysql_error());
  mysql_select_db($DB,$dbhandle) or die('Could not select database');


#! /usr/local/bin/perl5
use DBI;
our $dsn = "DBI:mysql:database=test_ipv6_websters_computers_com;host=localhost";              
our $user = "user";
our $password = "password";
our $dbh = DBI->connect($dsn, $user, $password);
die "Failed to connect to mysql" unless ($dbh);

now edit "/home/" and change line 34 to (change for your setup)

require "/home/";

now edit "/home/" and change line 31 to (change for your setup)

require "/home/";

now edit "/home/" and change line 34 to (change for your setup)

require "/home/";

also i had to change line 119 to (changed due to centos running an older version of rrdtools).

    my ($ref) = RRDs::graph( "$IMGDIR/$name", @args );

now edit "/home/" and change line 38 to (change for your setup)

require "/home/";

now edit "/home/" and change line 6 to (change for your setup)

 cd /home/

and line 10 and 11

    rsync -a daily_images/  /home/  &&
    rsync -a daily_images/  /home/

setup a cron job to run "/home/" i have it running every 5 minutes but hourly is probably fine as well.

If you want to put your company logo on the site you need to edit "/home/" mine now looks like this

<div id="about">
<p><img src="/images/websters-computers-logo2.png" alt="websters computers logo" width="728" height="105" /><br />
This mirror is provided by <a target="_blank" href="">Websters Computers</a></p>
<p>  Copyright (C) 2010, 2011 Jason Fesler. All rights reserved. -- r[% GET svn_Revision %]<br/>
 <a href="mirrors.html">Mirrors</a> |  <a href="partnering.html">Partnering</a> | <a href="mission.html">Mission</a> | <a href="source.html">Source</a> | <a href="">Email</a>
 - &nbsp; - 
<a href="attributions.html">Attributions</a>
[% IF page == "index" %]  |  <a href="#" onclick="GIGO.showdebug()">Debug</a> [% END %]
[% PROCESS "" %]

Now we can finally build the files.

cd /home/

They should now be stored in "cd /home/"
so we can can now copy them to the web server.

cd /home/
cp -v *.* /home/

Now go and test your site, everything should be working.
If you get any errors or want to have you mirror added as a public mirror post on the Mailling list

You will need to keep updating the site as there is loads of changes happening.
To make this easier for myself i created a cron job to do this for me, its runs once a day (just after midnight) and emails me the results.

touch /home/
chmod +x /home/

Now edit "/home/" and make it look like this.

cd /home/
rm *.*
cd /home/
svn checkout falling-sky-read-only
cd /home/
cd /home/
cp -v *.* /home/

The first 2 lines clean out our build folder, the next 2 lines update the source files with any changes. The next 2 lines rebuild the files and the last 2 lines moves them to our web server.

finally create a cron job to run "/home/" and tell it to email the results (only need to run once a day).

That's it, don't forget to check its all still working regularly.