Updated 2008-28-10. Proper version control is a must for everyone who programs more than a few lines of code. Even if you develop your applications all by yourself it is very handy to be able to branch and merge your code, be able to roll back to previous versions or undo changes you made in the past. It works great for regular applications, but managing web applications or websites is a tad harder for two reason: You need a webserver to get your application going and you usually have to manage database revisions as well.
Keeping database revisions in sync with your code revisions is a complex subject that I will leave until another time. In this article I will show you how you can configure your own computer or development server in such a way that checking out or deploying a web application is just as easy as any other piece of code.
First I will show you how to configure Apache on your development server so that it picks up your checked out working copies as separate subdomains. Using this, you can simply make a checkout of your project and it will automagically be up and running. No need to touch the Apache configuration. After that I will show you how to use dnsmasq so you can achieve the same effect on your own development machine. That way you can develop your web applications locally and you won't need a central development server. In my examples I will be assuming you use subversion for your version control, but it works virtually the same with other version control packages, such as git or bazaar.
Creating subdomains for every working copy
Here is what we want to achieve. Suppose that you have a development server at http://dev.example.org where you develop your websites. We want that every working copy is it's own subdomain on that server. If I check out a working copy and name it “foobar” then it should be available on http://foobar.dev.example.org. In order for this to work, you need a wildcard DNS entry so that all subdomains of dev.example.org go to your development server. I won't cover how you do that. If you do not run your own DNS server, ask your network administrator about it or use dnsmasq as explained in the second part of this article.
Start by creating a directory where all your working copies will live. For this article I will assume that this is /home/checkout. Every directory under this directory will become a subdomain on your development server. Create a trunk directory under the checkouts directory (/home/checkout/trunk). This is where the main dev.example.org domain will go to. When a subdomain does cannot resolve to a directory under /home/checkouts we want to show an error page. I created /home/checkouts/error.html containing some useful information, but you could also redirect it to a 404 error if you want.
Then create a new virtual host configuration for Apache.
- <VirtualHost *:80>
- DocumentRoot /home/checkout/trunk
- ServerName dev.example.org
- ServerAlias *.dev.example.org
- LogLevel warn
- ErrorLog /var/log/apache2/error.log
- CustomLog /var/log/apache2/access.log combined
- ServerSignature On
- </VirtualHost>
We will use mod_rewrite to tell Apache about all the subdomains. The following mod_rewrite rules extract the subdomain from the request, see if a directory exists in /home/checkout that matches the subdomain and then redirects the request to that directory (hat-tip to Stuart).
- RewriteEngine On
- # If the request contains a subdomain and the directory exists, redirect
- RewriteCond %{HTTP_HOST} ^([^\.]+)\.dev\.example\.org
- RewriteCond /home/checkout/%1 -d
- RewriteRule ^(.*) /home/checkout/%1/$1 [L]
Next, we add some more rules to redirect any non-existing subdomains to the error page:
- # Redirect non-existing subdomains to the error page
- RewriteCond %{HTTP_HOST} ^([^\.]+)\.dev\.example\.org
- RewriteRule ^(.*) /home/checkout/error.html [L]
Update: Kevin points out in this comment that using the VirtualDocumentRoot directive is an even nicer solution than these rewrite rules.
Finally we want to hide the .svn directories that subversion creates when you make a new checkout. If you use a different version control system such as git, then replace .svn with .git in the configuration below:
- # Hide .svn directories in checkouts
- <Directory ~ "/\.svn/">
- order allow,deny
- deny from all
- </Directory>
Restart Apache and you're done. Now you can simply log into the development server, make a new checkout in the /home/checkout directrory and you can immediately browse to it. After executing the example below, you can simply browse to http://my-working-copy.dev.example.org and see your website.
- $ ssh dev.example.org
- Last login: Wed Aug 6 09:36:33 2008 from 10.0.0.99
- dev$ cd /home/checkout
- dev$ svn checkout https://svn.example.org/project/trunk my-working-copy
The full Apache configuration file now looks like this (download):
- <VirtualHost *:80>
- DocumentRoot /home/checkout/trunk
- ServerName dev.example.org
- ServerAlias *.dev.example.org
- LogLevel warn
- ErrorLog /var/log/apache2/error.log
- CustomLog /var/log/apache2/access.log combined
- ServerSignature On
- RewriteEngine On
- # If the request contains a subdomain and the directory exists, redirect
- RewriteCond %{HTTP_HOST} ^([^\.]+)\.dev\.example\.org
- RewriteCond /home/checkout/%1 -d
- RewriteRule ^(.*) /home/checkout/%1/$1 [L]
- # Redirect non-existing subdomains to the error page
- RewriteCond %{HTTP_HOST} ^([^\.]+)\.dev\.example\.org
- RewriteRule ^(.*) /home/checkout/error.html [L]
- # Hide .svn directories in checkouts
- <Directory ~ "/\.svn/">
- order allow,deny
- deny from all
- </Directory>
- </VirtualHost>
Developing locally with dnsmasq
Developing your websites on a central development server works, but most often it's easier to develop on your local machine instead. It's usually faster and there are quite a few handy tools out there that don't work transparently over an ssh connection. When you have a Linux desktop or laptop it's dead easy to do. Simply install Apache, PHP, MySQL or whatever server-side tools you need on your desktop, copy the configuration files from the server and modify them.
The only problem with using the above Apache configuration locally is the DNS wildcard. Unless your desktop is assigned a hostname by your network's DNS server and you can set the wildcard there, you will have to make do with your localhost address. You can install dnsmasq to act as a local caching DNS server and put the wildcard on your own machine. As a bonus, the DNS caching can also speed up your web browsing (hat-tip to Carthik). If you want, you can replace localhost with another name (like your hostname) in the commands below. The commands below are for Debian Lenny, but should be similar on other distributions. Let's start my installing dnsmasq.
- # apt-get install dnsmasq
Now we edit /etc/dnsmasq.conf So that it listens on 127.0.0.1 for our DNS requests and tell it to point all subdomains of localhost to 127.0.0.1. Near line 92 you will find the listen-address configuration, which tells dnsmasq to listen for DNS requests. Add the following line:
- listen-address=127.0.0.1
You can force domains to resolve to a certain IP with the address directive, which you find near line 63. Add the following line to make localhost have all it's subdomains pointing to 127.0.0.1:
- address=/localhost/127.0.0.1
Most likely your machine connects to the network via DHCP and gets it's nameservers from the DHCP server. We need to configure DHCP so that it always asks the local dnsmasq server first before trying the other DNS servers. Open up /etc/dhcp3/dhclient.conf and uncomment the following line:
- prepend domain-name-servers 127.0.0.1;
The next time you connect to the network, the DHCP client will add 127.0.0.1 as a nameserver to /etc/resolv.conf, before the other nameservers. If you do not want to reconnect to the network to get this change, you can add it manually for now. Open up /etc/resolv.conf and add the following line before the nameserver directives, but after the search directive:
- nameserver 127.0.0.1
Restart dnsmasq for the changes to take effect:
- # /etc/init.d/dnsmasq restart
- Restarting DNS forwarder and DHCP server: dnsmasq.
Now you can configure Apache according to the first part of this article, replacing dev.example.org with localhost. Do not forget to remove Apache's default virtual host, which also listens on localhost.
- # rm /etc/apache2/sites-enabled/001-default
Reload Apache and now you can simply make a checkout of your website to your local machine and immediately view it in your browser by going to my-working-copy.localhost. Happy coding!
Comments
#1 Anonymous Coward
#2 xyz
1. one standalone managed server with channel name
2. virtual host name with network access point name as same as channel name of server and targetted to managed server.
3. in c:/windows/system32/etc/hosts i have given my host name like www.son.com
4.deployed application on virtual hosts,the error i am getting is---comments:None of the targets for this Deployment are running. No test points possible
plz find a solution for this, whether i have to configure apache for this to work so that i can deploy application on virtual host.
by,
xyz
#3 Sander Marechal (http://www.jejik.com)
#4 Who
#5 Anon
#6 Sander Marechal (http://www.jejik.com)
@Anon: If you're interested in running Subversion under Apache, you might like my older article: Apache and Subversion authentication with Microsoft Active Directory. It not only explains how to make Subversion go under Apache, but also explains how you can authenticate Subversion users through Active Directory.
#7 dan (http://cssgallery.info)
I would like to know an "easy" solution for hosted development environments, where i cannot alter the apache configuration. Thank you.
#8 Lo c Hoguin (http://wee.extend.ws)
#9 Sander Marechal (http://www.jejik.com)
Well, you could ask the hosting company to change the Apache configuration for you. All they would need to do is change the ServerAlias line and put the "*." in front of the domain name. You could put all the other stuff in a .htaccess file in the DocumentRoot directory.
But if you're serious about web development then you should really get your own development server to play with. It doesn't have to cost any money. You don't even need an internet connection. Get any old Pentium III or better (you can find those for free. Lots of people throw these old machines out) then install Linux and hook it into your home network.
You can make the checkouts and do all your development on your home server. No internet required! Then once in a while you make a checkout and (s)ftp it to your shared hosting account to update your production website.
#10 Anonymous Coward
So lets say I am working on a site for acme, I will use the standard form of "acme.local" for the domain, the /etc/hosts syntax is
127.0.0.1 acme.local
Then I configure my Apache/nginx/lighttpd vhost to listen for "acme.local" and boom, you're done.
#11 Sander Marechal (http://www.jejik.com)
That won't work with the above setup. Your /etc/hosts file does not support wildcards. If you put "127.0.0.1 acme.local" in your hosts file then "trunk.acme.local" or "my-branch.acme.local" will not resolve to 127.0.0.1. That's why you need dnsmasq. You need to be able to resolve "*.acme.local".
#12 Kevin
Question - I would like a request to: project1.example.org not to go to /home/checkout/project1 but to /home/checkout/project1/web
I am having trouble getting this to work - I don't have a lot of experience with mod_rewrite
Any Help?
#13 Kevin
RewriteRule ^(.*) /home/checkout/%1/$1 [L]
to
RewriteRule ^(.*) /home/checkout/%1/web/$1 [L]
This seems to work. What I am trying to do is get a symfony framework project working... Since symfony does its own Rewrite I think I am putting it into an infinite rewrite loop or something. I get a "500 Internal Server Error" when I try to test the project (sf_sandbox.example.com which redirects to /home/checkout/sf_sandbox/web) - When I look in apache's error log I see this error:
[Mon Oct 27 10:03:02 2008] [error] [client 10.0.2.15] Request exceeded the limit of 20 internal redirects due to probable configuration error. Use 'LimitInternalRecursion' to increase the limit if necessary. Use 'LogLevel debug' to get a backtrace.
Any idea what I can do? Do you have any experience with getting a symfony project to work with this configuration?
#14 Sander Marechal (http://www.jejik.com)
I haven't used Symphony before. I donwloaded their source code and looked at the .htaccess file in the /web/ directory. There is a commented out "RewriteBase /" directive there. Did you try uncommenting that?
If that doesn't work then you can also modify Symphony's .htaccess file to point to the correct location. However, when you do that then it will only work on your development server. When you deploy to some different server somewhere else then you need to modify the .htaccess again. If you want to try it, replace this:
With this:
Note: I haven't tested this, but I *think* it should do the trick. Let me know if you still have problems with it.
#15 Kevin
Thanks for the quick reply!
Uncommenting the "RewriteBase /" seems to have done the trick!
What exactly did this do? Was I correct in assuming it was in an endless rewrite loop?
I really appreciate your help!
-Kevin
#16 Kevin
I placed "Alias /sf /usr/share/php/data/symfony/web/sf" in the VHost file but it doesn't seem to work - any ideas?
This is almost coming together.
Kevin
#17 Sander Marechal (http://www.jejik.com)
Yes. Remember that when you rewrite an URL to a different directory with .htaccess, all the rewrite rules are applied *again*. So when the request got rewritten to index.php then the rewrite deployment script would kick in again, and then the .htaccess again, etcetera, etcetera.
I am guessing that the alias happens before the rewrite rules are applied. So, /usr/share/php/data/symfony/web/sf gets rewritten to /home/checkout/your-checkout/sf/ again. There are multiple ways to solve this. You could make /sf a symlink on your filesystem to the correct directory. Alternatively, you could add a RewriteRule that catches the /sf before any of the other rules so. Try adding something like this after the "RewriteEngine" on in the virtual host configuration (leave the Alias in place):
That should catch any request to /sf do nothing with it and stop processing the other rules.
#18 Kevin
I am not able to get this to work without a symlink...
I have the Alias right before the "RewriteEngine On" and "RewriteRule ^/sf$ - [L,QSA]" directly after.
I did find another way of doing it with VirtualDocumentRoot. Does it in one line "VirtualDocumentRoot /home/checkout/%1/web/" and the Alias works.
Any reason I shouldn't be using this?
Thanks,
Kevin
#19 Sander Marechal (http://www.jejik.com)
#20 Kevin
#21 Sander Marechal (http://www.jejik.com)
That should only catch non-existing subdomains, leaving the existing subdomains to be handled by VirtualDocumentRoot.
#22 Sean Gravener (http://sean.gravener.net)
#23 Anonymous Coward
thanks for the article! I'm stuck at the following point "Then create a new virtual host configuration for Apache." Where do all those changes need to be applied? Which Files?
Kind regards,
one who is ashamed of not knowing APACHE and his new Ubuntu 10.10. that well....
#24 Sander Marechal (http://www.jejik.com)
Comments have been retired for this article.