SaltyCrane: cardstorehttps://www.saltycrane.com/blog/2009-10-22T00:44:17-07:00Notes on switching my Djangos to mod_wsgi
2009-10-22T00:44:17-07:00https://www.saltycrane.com/blog/2009/10/notes-switching-my-djangos-mod_wsgi/<p>I'm slowly trying to make my Django web servers conform to current
best practices. I've
<a href="/blog/2009/04/notes-using-nginx-mod_python-and-django/">
set up an Nginx reverse proxy for serving static files</a>,
<a href="/blog/2009/05/notes-using-pip-and-virtualenv-django/">
started using virtualenv to isolate my Python environments</a>, and
<a href="/blog/2009/08/notes-migrating-blog-sqlite-postgresql/">
migrated my database to PostgreSQL</a>. I ultimately want to implement
<a href="http://bretthoerner.com/blog/2008/oct/27/using-nginx-memcached-module-django/">
memcached+Nginx caching</a> in my reverse proxy, but the next task on my to-do list
is switching from mod_python to <a href="http://code.google.com/p/modwsgi/">mod_wsgi</a>.
</p>
<p>
Within the past year (or maybe before), mod_wsgi has become the
<a href="http://docs.djangoproject.com/en/dev/howto/deployment/modwsgi/">
preferred</a>
<a href="http://simonwillison.net/2009/Apr/1/modwsgi/">method</a>
for serving Django applications. I also originally thought
<a href="http://collingrady.wordpress.com/2009/01/06/mod_python-versus-mod_wsgi/">
switching
from mod_python</a> to mod_wsgi would save me some much needed memory on my
256MB VPS. But after trying it out, running with a single Apache process
in each case, the memory footprint was about the same. Even switching
from mod_wsgi's embedded mode to daemon mode didn't make a significant difference.
Likely the performance is better with mod_wsgi, though.
</p>
<p>Here are my notes on installing mod_wsgi.</p>
<h4>Configuration References</h4>
<ul>
<li>Django docs: <a href="http://docs.djangoproject.com/en/dev/howto/deployment/modwsgi/#howto-deployment-modwsgi">
How to use Django with Apache and mod_wsgi</a></li>
<li>mod_wsgi docs: <a href="http://code.google.com/p/modwsgi/wiki/QuickConfigurationGuide">
Quick Configuration Guide</a></li>
<li>A good blog article: <a href="http://bretthoerner.com/blog/2008/oct/09/configs-nginx-and-apache-mod_wsgi/">
Configs for nginx and Apache with mod_wsgi</a></li>
</ul>
<h4>Advice from mod_wsgi author Graham Dumpleton</h4>
<ul>
<li><a href="http://blog.dscpl.com.au/2009/03/load-spikes-and-excessive-memory-usage.html">
Load spikes and excessive memory usage in mod_python</a></li>
<li><a href="http://blog.dscpl.com.au/2008/12/using-modwsgi-when-developing-django.html">
Using mod_wsgi when developing Django sites</a></li>
<li><a href="http://groups.google.com/group/django-users/browse_thread/thread/6d670b0fa7c0d733/4ff111a1f00f7629?q=worker+daemon#4ff111a1f00f7629">
http://groups.google.com/group/django-users/browse_thread/thread/6d670b0fa7c0d733/4ff111a1f00f7629?q=worker+daemon#4ff111a1f00f7629</a></li>
<li><a href="http://groups.google.com/group/modwsgi/browse_thread/thread/cb21864a97d44ee9/38716433921a48cb?q=worker+daemon#38716433921a48cb">
http://groups.google.com/group/modwsgi/browse_thread/thread/cb21864a97d44ee9/38716433921a48cb?q=worker+daemon#38716433921a48cb</a></li>
</ul>
<h4>Install mod_wsgi and apache mpm-worker</h4>
<p>I'm not 100% sure about prefork vs. worker mpm, but Graham Dumpleton
favors worker mpm.</p>
<pre>sudo apt-get install libapache2-mod-wsgi
sudo apt-get install apache2-mpm-worker</pre>
<h4>Create .wsgi application file</h4>
<p>My virtualenv is located at <code>/srv/python-environments/saltycrane</code>.
My Django settings files is at <code>/srv/SaltyCrane/iwiwdsmi/settings.py</code>.
</p>
<p><code>/srv/SaltyCrane/saltycrane.wsgi</code>:</p>
<pre class="python">import os
import sys
import site
site.addsitedir('/srv/python-environments/saltycrane/lib/python2.5/site-packages')
os.environ['DJANGO_SETTINGS_MODULE'] = 'iwiwdsmi.settings'
sys.path.append('/srv/SaltyCrane')
import django.core.handlers.wsgi
application = django.core.handlers.wsgi.WSGIHandler()</pre>
<h4>Edit Apache's httpd.conf file</h4>
<p>I went back and forth between using embedded mode or daemon mode.
I've ended up with embedded mode for now since it seems to use a tad less
memory and is supposed to be a little bit faster. However, Graham Dumpleton seems to
recommend daemon mode for people on VPSs. I may change my mind again later.
To use daemon mode, I just need to uncomment the <code>WSGIDaemonProcess</code>
and <code>WSGIProcessGroup</code> lines.
I have <code>StartServers</code> set to 1 because I can only afford to have
one Apache process running.
This is assuming nginx is proxying requests to apache. For more
on my nginx setup, see <a href="/blog/2009/04/notes-using-nginx-mod_python-and-django/">
here</a>.</p>
<p>Edit <code>/etc/apache2/httpd.conf</code>:</p>
<pre>
<IfModule mpm_worker_module>
StartServers 1
ServerLimit 1
ThreadsPerChild 5
ThreadLimit 5
MinSpareThreads 5
MaxSpareThreads 5
MaxClients 5
MaxRequestsPerChild 500
</IfModule>
KeepAlive Off
NameVirtualHost 127.0.0.1:8080
Listen 8080
<VirtualHost 127.0.0.1:8080>
ServerName www.saltycrane.com
# WSGIDaemonProcess saltycrane.com processes=1 threads=5 display-name=%{GROUP}
# WSGIProcessGroup saltycrane.com
WSGIScriptAlias / /srv/SaltyCrane/saltycrane.wsgi
</VirtualHost>
<VirtualHost 127.0.0.1:8080>
ServerName supafu.com
# WSGIDaemonProcess supafu.com processes=1 threads=5 display-name=%{GROUP}
# WSGIProcessGroup supafu.com
WSGIScriptAlias / /srv/Supafu/supafu.wsgi
</VirtualHost>
<VirtualHost 127.0.0.1:8080>
ServerName handsoncards.com
# WSGIDaemonProcess handsoncards.com processes=1 threads=5 display-name=%{GROUP}
# WSGIProcessGroup handsoncards.com
WSGIScriptAlias / /srv/HandsOnCards/handsoncards.wsgi
</VirtualHost>
</pre>
<h4>Restart Apache</h4>
<pre>sudo /etc/init.d/apache2 restart</pre>
Notes on using nginx with mod_python and Django
2009-04-28T00:26:11-07:00https://www.saltycrane.com/blog/2009/04/notes-using-nginx-mod_python-and-django/<p>Here are my notes on setting up
<a href="http://nginx.net/">nginx</a> as a reverse proxy with
Apache, mod_python, and Django on Ubuntu Intrepid. Nginx is used
to serve my static media files while all other requests are passed
on to the Apache/mod_python/Django web server.
</p>
<p>I realize
<a href="http://code.google.com/p/modwsgi/">mod_wsgi</a> has become the
<a href="http://simonwillison.net/2009/Apr/1/modwsgi/">preferred</a>
<a href="http://collingrady.wordpress.com/2009/01/06/mod_python-versus-mod_wsgi/">
way</a> to deploy Django, but I'm a little behind the times and am still
using mod_python. I hope to switch to mod_wsgi soon.
</p>
<p>I <a href="http://www.saltycrane.com/blog/2008/12/card-store-project-4-notes-using-amazons-cloudfront/">
have been using</a> Amazon's
<a href="http://aws.amazon.com/cloudfront/">CloudFront</a> service for
delivering my static media files. As far as I can tell, it has worked well.
My main reason for switching to nginx is so I can skip the extra step of
uploading to Amazon. Regarding my concern about the memory footprint of nginx,
it looks like it is using around 5mb with my two process configuration.
</p>
<p>My configuration parameters are shown below. I'm running two sites, SaltyCrane and
HandsOnCards on a <a href="http://www.slicehost.com/">Slicehost</a> 256mb plan.
</p>
<table width=550>
<tr>
<td><b>Description</b></td>
<td><b>HandsOnCards</b></td>
<td><b>SaltyCrane</b></td>
</tr>
<tr>
<td>Redirection</td>
<td><code class="code2">http://www.handsoncards.com</code> is redirected to <code class="code2">http://handsoncards.com</code></td>
<td><code class="code2">http://saltycrane.com</code> is redirected to <code class="code2">http://www.saltycrane.com</code></td>
</tr>
<tr>
<td>Static media filesystem path</td>
<td><code class="code2">/srv/HandsOnCards/handsoncards/static/</code></td>
<td><code class="code2">/srv/SaltyCrane/iwiwdsmi/media/</code></td>
</tr>
<tr>
<td>Static media web path</td>
<td><code class="code2">/site_media/</code></td>
<td><code class="code2">/site_media/</code></td>
</tr>
<tr>
<td>Django settings.py file location</td>
<td><code class="code2">/srv/HandsOnCards/handsoncards/settings.py</code></td>
<td><code class="code2">/srv/SaltyCrane/iwiwdsmi/settings.py</code></td>
</tr>
<tr>
<td>Additional Python packages path</td>
<td><code class="code2">/srv/python-packages</code></td>
<td><code class="code2">/srv/python-packages</code></td>
</tr>
</table>
<h4>Install nginx</h4>
<p>Some recommend installing nginx from source, but I took the easier route
and used Ubuntu's package manager.</p>
<pre>sudo apt-get install nginx</pre>
<h4>Nginx configuration</h4>
<p>Edit <code>/etc/nginx/nginx.conf</code>:</p>
<pre class="nginx">user www-data www-data;
worker_processes 2;
error_log /var/log/nginx/error.log;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
access_log /var/log/nginx/access.log;
sendfile on;
tcp_nopush on;
keepalive_timeout 3;
tcp_nodelay off;
gzip on;
gzip_comp_level 2;
gzip_proxied any;
gzip_types text/plain text/html text/css application/x-javascript text/xml
application/xml application/xml+rss text/javascript;
server {
listen 80;
server_name www.handsoncards.com;
rewrite ^/(.*) http://handsoncards.com/$1 permanent;
}
server {
listen 80;
server_name handsoncards.com;
access_log /var/log/nginx/handsoncards.com.access.log;
error_log /var/log/nginx/handsoncards.com.error.log;
location / {
proxy_pass http://127.0.0.1:8080/;
include /etc/nginx/proxy.conf;
}
location /site_media/ {
alias /srv/HandsOnCards/handsoncards/static/;
expires 24h;
}
}
server {
listen 80;
server_name saltycrane.com;
rewrite ^/(.*) http://www.saltycrane.com/$1 permanent;
}
server {
listen 80;
server_name www.saltycrane.com;
access_log /var/log/nginx/saltycrane.com.access.log;
error_log /var/log/nginx/saltycrane.com.error.log;
location / {
proxy_pass http://127.0.0.1:8080/;
include /etc/nginx/proxy.conf;
}
location /site_media/ {
alias /srv/SaltyCrane/iwidsmi/media/;
expires 24h;
}
}
}
</pre>
<p>Edit <code>/etc/nginx/proxy.conf</code>:</p>
<pre class="nginx">proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
client_max_body_size 10m;
client_body_buffer_size 128k;
proxy_connect_timeout 90;
proxy_send_timeout 90;
proxy_read_timeout 90;
proxy_buffers 32 4k;</pre>
<p>Restart Nginx</p>
<pre>sudo /etc/init.d/nginx restart</pre>
<h4>Install Apache</h4>
<p>I already had Apache and mod_python installed, but in case you don't:</p>
<pre>apt-get install apache2 apache2-mpm-prefork
apt-get install libapache2-mod-python</pre>
<h4>Apache Configuration</h4>
<p>Edit <code>/etc/apache2/httpd.conf</code>:</p>
<pre>MaxClients 2
MaxRequestsPerChild 350
KeepAlive Off
NameVirtualHost 127.0.0.1:8080
Listen 8080
<VirtualHost 127.0.0.1:8080>
ServerName www.saltycrane.com
<Location "/">
SetHandler python-program
PythonHandler django.core.handlers.modpython
SetEnv DJANGO_SETTINGS_MODULE iwiwdsmi.settings
PythonPath "['/srv/SaltyCrane', '/srv/python-packages'] + sys.path"
PythonDebug Off
</Location>
</VirtualHost>
<VirtualHost 127.0.0.1:8080>
ServerName handsoncards.com
<Location "/">
SetHandler python-program
PythonHandler django.core.handlers.modpython
SetEnv DJANGO_SETTINGS_MODULE handsoncards.settings
PythonPath "['/srv/HandsOnCards', '/srv/python-packages'] + sys.path"
PythonDebug Off
</Location>
</VirtualHost></pre>
<p>Edit <code>/etc/apache2/ports.conf</code> and comment out the following two lines:</p>
<pre>#NameVirtualHost *:80
#Listen 80</pre>
<p>Restart Apache</p>
<pre>sudo /etc/init.d/apache2 restart</pre>
<h4>Add Django's reverse proxy middleware</h4>
<p>Edit your <code>settings.py</code> file to include
<code class="code2">django.middleware.http.SetRemoteAddrFromForwardedFor</code> in
<code>MIDDLEWARE_CLASSES</code>. This allows your Django application to use the real
IP address of the client instead of <code>127.0.0.1</code> from your nginx proxy. It
sets Django's <code>request.META['REMOTE_ADDR']</code>
to be <code>request.META['HTTP_X_FORWARDED_FOR']</code> which we set above in nginx's
<code>proxy.conf</code>. For more information, see the
<a href="http://docs.djangoproject.com/en/dev/ref/middleware/#reverse-proxy-middleware">
Middleware reference</a> in the Django documentation.
</p>
<pre>MIDDLEWARE_CLASSES = (
'django.middleware.common.CommonMiddleware',
'django.middleware.http.SetRemoteAddrFromForwardedFor',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
)</pre>
<h4>References</h4>
<ul>
<li><a href="http://bart.whahay.net/blog/2009/04/06/setting-up-nginx-django.html">
http://bart.whahay.net/blog/2009/04/06/setting-up-nginx-django.html</a> (my main reference)</li>
<li><a href="http://articles.slicehost.com/2009/3/5/ubuntu-intrepid-nginx-configuration">
http://articles.slicehost.com/2009/3/5/ubuntu-intrepid-nginx-configuration</a> (general nginx configuration)</li>
<li><a href="http://articles.slicehost.com/2009/3/6/ubuntu-intrepid-nginx-virtual-hosts-1">
http://articles.slicehost.com/2009/3/6/ubuntu-intrepid-nginx-virtual-hosts-1</a> (nginx virtual hosts configuration)</li>
<li><a href="http://forum.slicehost.com/comments.php?DiscussionID=2964">
http://forum.slicehost.com/comments.php?DiscussionID=2964</a> (explains the
difference between <code>root</code> and <code>alias</code> in nginx.conf.)</li>
</ul>
<h4>Errors</h4>
<ul>
<li>A 502 Bad Gateway from Nginx probably means there is something wrong with your Apache setup</li>
<li>An Internal Server Error from Apache means something is probably wrong with your Django
setup.</li>
</ul>
Card store project #5: Redirecting my www-prefixed domain to my non-www-prefixed domain
2008-12-28T16:53:07-08:00https://www.saltycrane.com/blog/2008/12/card-store-project-5-redirecting-my-www-prefixed-domain-my-non-www-prefixed-domain/<p>For search engine optimization and analytics, I wanted to make
<a href="http://www.handsoncards.com/">http://www.handsoncards.com/</a>
permanently redirect to <a href="http://handsoncards.com/">http://handsoncards.com/</a>.
So I started Googling. It took me a while to figure this one out... The first few articles I read
suggested using Rewrite directives in my <code>.htaccess</code> file but this didn't work for me
(maybe because I'm using mod_python and Django?). Then I found I could use
Rewrite in my <code>httpd.conf</code> file, but I got an infinite redirect loop.
Finally, I found the solution from <a href="http://www.webmasterworld.com/apache/3217088.htm">
this discussion</a>. Here's what I did. I'm using Django, mod_python, and
Apache on Ubuntu at Slicehost.
</p>
<ul>
<li>Enable mod_rewrite. Logged in as root:
<pre>a2enmod rewrite</pre>
</li>
<li>Edit <code>/etc/apache2/httpd.conf</code>:
<pre><VirtualHost *>
ServerName handsoncards.com
ServerAlias www.handsoncards.com
#
RewriteEngine on
RewriteCond %{HTTP_HOST} ^www\.handsoncards\.com
RewriteRule (.*) http://handsoncards.com$1 [R=301,L]
#
SetHandler python-program
PythonHandler django.core.handlers.modpython
SetEnv DJANGO_SETTINGS_MODULE handsoncards.settings
PythonPath "['/srv/HandsOnCards', '/srv/python-packages'] + sys.path"
PythonDebug Off
</VirtualHost></pre>
</li>
<li>Restart Apache:
<pre>/etc/init.d/apache2 restart</pre>
</li>
</ul>
Card store project #4: Notes on using Amazon's CloudFront
2008-12-27T17:22:45-08:00https://www.saltycrane.com/blog/2008/12/card-store-project-4-notes-using-amazons-cloudfront/<p>I haven't been keeping up with the current events very well recently, but
I haven't noticed a lot of people using Amazon's
<a href="http://aws.amazon.com/s3/">S3</a> or
<a href="http://aws.amazon.com/cloudfront/">CloudFront</a> with Django on
VPS hosting. Though there is <a href="http://holovaty.com/blog/archive/2006/04/07/0927">
Adrian's post</a> from 2006, I see more articles about serving media
files with <a href="http://www.lighttpd.net/">lighttpd</a> or, more recently,
<a href="http://wiki.codemongers.com/Main">nginx</a>. Is a CDN unnecessary
for our needs? I thought it'd be good to take some load off my VPS server
since I need all the memory I can get for my Django web server and database.
But maybe web servers such as nginx are so lightweight it doesn't make much
of an impact? I didn't think the cost would be too much-- on this blog, I'm
only paying about $0.10/month for S3 services to serve my static media. Of course,
there isn't a lot of static media to serve on this blog, but it still seems
like it would be a fraction of the $20/month I'm paying for VPS at
<a href="http://www.slicehost.com/">Slicehost</a>. It may be the convenience
factor-- because every time I update a static file, I then have to upload it
to S3. This is even more inconvenient for files uploaded through the admin
interface. I think some people have probably solved this already... maybe using
Django signals. Maybe it is a combination of all these things. Please let me know what
you think. If you're not using S3/CloudFront, why aren't you?</p>
<p>Well I went ahead and gave CloudFront a try since it is so easy. My card store project
website seems to
be somewhat faster than before. Please check it out
<a href="http://handsoncards.com/">here</a>.
I'm still not sure if I should be happy with the site's speed though. I did a quick
<a href="http://www.danga.com/memcached/">memcached</a> install, but I don't
think I've configured it properly. I will probably need to revisit that.
Anyways, here are my notes on using
CloudFront with my <a href="http://www.satchmoproject.com/">Satchmo</a> store.</p>
<h4>Sign up for S3</h4>
<ul>
<li>Sign up for <a href="http://aws.amazon.com/">Amazon
Web Services</a></li>
<li>Sign up for <a href="http://aws.amazon.com/s3/">Simple Storage
Service</a></li>
<li>Take note of your "Access Key ID" and your "Secret Access Key" under
"Your Account", "Access Identifiers"</li>
</ul>
<h4>Get S3 Python library</h4>
<ul>
<li>Download the <a href="http://developer.amazonwebservices.com/connect/entry.jspa?externalID=134">
Amazon S3 Python library</a></li>
<li>Unpack it, and put <code>s3-example-libraries/python/S3.py</code>
<a href="http://www.saltycrane.com/blog/2008/08/somewhere-your-python-path/">somewhere on your
Python path</a>.</li>
</ul>
<h4>Create a S3 bucket</h4>
<ul>
<li>Create a file named <code>create_bucket.py</code>:
<pre>import S3
ACCESS_KEY = 'myaccesskey'
SECRET_KEY = 'mysecretaccesskey'
BUCKET_NAME = 'handsoncards'
conn = S3.AWSAuthConnection(ACCESS_KEY, SECRET_KEY)
conn.create_bucket(BUCKET_NAME)</pre>
</li>
<li>Run it:
<pre>python create_bucket.py</pre>
</li>
</ul>
<h4>Upload files to S3</h4>
<ul>
<li>Download <a href="http://www.holovaty.com/code/update_s3.py">Adrian's S3 upload
script</a> and save it to <code>/srv/HandsOnCards/handsoncards/bin/update_s3.py</code></li>
<li>Edit the script with the correct values for <code>AWS_ACCESS_KEY_ID</code>,
<code>AWS_SECRET_ACCESS_KEY</code>, and <code>BUCKET_NAME</code>.</li>
<li>Upload files. (Assumes static directory is linked to <code>/var/www/site_media</code>).
<pre>cd /var/www
find -L site_media | grep -v '~$' | python /srv/HandsOnCards/handsoncards/bin/update_s3.py
find -L admin_media | grep -v '~$' | python /srv/HandsOnCards/handsoncards/bin/update_s3.py</pre>
</li>
</ul>
<h4>Set up CloudFront</h4>
<ul>
<li>Sign up for CloudFront</li>
<li>Get the S3 Fox firefox plugin</li>
<li>Click "Manage Accounts" and enter access key and secret key</li>
<li>Right click on your bucket (handsoncards) and select "Manage Distributions"
Enter a "Comment" and optional CNAME, then click "Create Distribution".
</li>
<li>Wait a while while the distribution is created. Take note of the
"Domain Name". For me it is: <code>http://d16z1yuk7jeryy.cloudfront.net</code>
</li>
<li>Click the refresh button until the "Status" says "Deployed"</li>
</ul>
<h4>Update settings and templates to use CloudFront</h4>
<ul>
<li>In settings.py set MEDIA_URL and ADMIN_MEDIA_PREFIX as follows:
<pre>MEDIA_URL = 'http://d16z1yuk7jeryy.cloudfront.net/site_media/'
ADMIN_MEDIA_PREFIX = 'http://d16z1yuk7jeryy.cloudfront.net/admin_media/'</pre>
</li>
<li>In your base.html template and all other templates, replace
<code>/site_media/</code> with
<code>http://d16z1yuk7jeryy.cloudfront.net/site_media/</code>.
</li>
</ul>
<h4>Update 2009-06-08: Add "Expires" headers</h4>
<p>For better performance, it is good to add a far-future "Expires" header to static
content on S3. To do this I modified Adrian's script to set the "Expires"
header to be one year in the future as shown below.
Thanks to <a href="#c2711">orip</a> for this tip.</p>
<pre><span style="color:red">from datetime import datetime, timedelta</span>
import mimetypes
import os.path
import sys
import S3 # Get this from Amazon
AWS_ACCESS_KEY_ID = 'CHANGEME'
AWS_SECRET_ACCESS_KEY = 'CHANGEME'
BUCKET_NAME = 'CHANGEME'
def update_s3():
conn = S3.AWSAuthConnection(AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY)
for line in sys.stdin:
filename = os.path.normpath(line[:-1])
if filename == '.' or not os.path.isfile(filename):
continue # Skip this, because it's not a file.
print "Uploading %s" % filename
filedata = open(filename, 'rb').read()
<span style="color:red">expires = datetime.utcnow() + timedelta(days=365)
expires = expires.strftime("%a, %d %b %Y %H:%M:%S GMT")</span>
content_type = mimetypes.guess_type(filename)[0]
if not content_type:
content_type = 'text/plain'
conn.put(BUCKET_NAME, filename, S3.S3Object(filedata),
{'x-amz-acl': 'public-read',
'Content-Type': content_type,
<span style="color:red">'Expires': expires,</span>
})
if __name__ == "__main__":
update_s3()</pre>
<br>
For more information:<br>
<ul>
<li><a href="http://developer.yahoo.com/performance/rules.html#expires">
"Add an Expires or a Cache-Control Header" section of the Yahoo Developer Network
"Best Practices for Speeding Up Your Web Site" guide</a></li>
<li><a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.21">
Section 14.21 of the HTTP specification</a></li>
<li><a href="http://www.drunkenfist.com/304/2007/12/26/setting-far-future-expires-headers-for-images-in-amazon-s3/">
Rob Larsen's blog post: "Setting Far Future Expires Headers For Images In Amazon S3"</a></li>
</ul>
<h4>Update 2009-10-21: Add CNAME record</h4>
<p>Go to your DNS Zone manager and add a CNAME record with the following parameters:</p>
<ul>
<li>Type: CNAME</li>
<li>Name: static</li>
<li>Data: d16z1yuk7jeryy.cloudfront.net. <em>(Don't forget the period at the end!)</li>
<li>TTL: <em>whatever you want. I left it at 86400</em></li>
</ul>
<p>Now wherever I previously would have used <code>http://d16z1yuk7jeryy.cloudfront.net</code>, I can replace it with <code>http://static.handsoncards.com</code>.</p>
Postgres backup with cron
2008-12-27T12:36:36-08:00https://www.saltycrane.com/blog/2008/12/postgres-backup-cron/<p>So I accidentally deleted my Postgres database on my public server instead
of my local server last night. Luckily, I was still in the testing phase.
I had been meaning to figure out how to backup Postgres but had been putting
it off. Now I've been provided the proper motivation.</p>
<p>The PostgreSQL documentation has a whole
<a href="http://www.postgresql.org/docs/8.3/interactive/backup.html">
chapter devoted to backup</a>. There it describes three methods of
backup: SQL dump, file system level backup, and continuous archiving.
I chose SQL dump.</p>
<h4>Manual SQL dump</h4>
<p>To do a manual dump, here is what I did. (As you might guess,
handsoncards_db is my database name.)</p>
<pre>su postgres
pg_dump handsoncards_db > /tmp/testdump
exit</pre>
<h4>Schedule backups with cron</h4>
<p>To perform backups at regular intervals I used cron.
Logged in as root, I created a file
<code>handsoncards_db_backup</code> in <code>/etc/cron.d</code>.
(Note, I'm running Ubuntu Intrepid. Cron will automatically start
running this new job without a restart.)
<pre>
# m h dom mon dow user command
45 3 * * * postgres pg_dump handsoncards_db > /srv/backups/handsoncards_db_backup
</pre>
<p>This will create a backup at 3:45am every day.
Be sure to put a newline at the end of the file.</p>
<p>Finally I created the backup directory and made postgres the owner.</p>
<pre>mkdir -p /srv/backups
chown postgres:postgres /srv/backups</pre>
<h4>Restore a database from the dump file</h4>
<p>If necessary, delete the old database. Then create a new databse and restore
from the dump file.</p>
<pre>su postgres
psql template1</pre>
<pre>CREATE DATABASE handsoncards_db OWNER django_user ENCODING 'UTF8';
\q</pre>
<pre>psql handsoncards_db < /srv/backups/handsoncards_db_backup
exit</pre>
<p>NOTE: if you get a permission denied error when trying to restore, check the
Unix permissions on the backup file and all the parent directories.</p>
Card store project #3: Installing Satchmo, part 2
2008-12-12T23:20:12-08:00https://www.saltycrane.com/blog/2008/12/card-store-project-3-installing-satchmo-part-2/<p>Here are my initial steps in setting up my <a href="http://www.satchmoproject.com/">
Satchmo</a> store. It is meant to be a continuation of my previous post,
<a href="http://www.saltycrane.com/blog/2008/12/card-store-project-2-installing-satchmo-django-postgresql-and-apache-ubuntu-slicehost/">
Installing Satchmo, Django, PostgreSQL, and Apache on Ubuntu at Slicehost</a>.
I am using Satchmo version 0.8, released 2008-11-25. I am combining the instructions
from the <a href="http://www.satchmoproject.com/docs/rel/0.8/new_installation.html">
Satchmo documentation</a> and
<a href="http://gosatchmo.com/starting-a-new-store-real-world-project-layout">Bruce's
blog post</a>. I'm sorry if this post is redundant-- I just wanted to have all
the notes in one place for my reference. Almost all the instructions here are from
one of these two sources.</p>
<p>Here is my final project directory structure mostly copied from Bruce's post:</p>
<pre>HandsOnCards/
`-- handsoncards/
|-- __init__.py
|-- bin/
|-- local_settings.py
|-- manage.py*
|-- satchmo.log
|-- settings.py
|-- static/
| |-- css/
| | |-- blackbird.css*
| | `-- style.css
| |-- images/
| | |-- blackbird_icons.png
| | |-- blackbird_panel.png
| | |-- productimage-picture-default.jpg
| | `-- sample-logo.bmp
| |-- js/
| | |-- blackbird.js
| | |-- jquery.cookie.js
| | |-- jquery.form.js
| | |-- jquery.js
| | |-- satchmo_checkout.js
| | |-- satchmo_core.js
| | |-- satchmo_pay_ship.js
| | `-- satchmo_product.js
| `-- protected/
|-- store/
| |-- __init__.py
| |-- models.py
| |-- templatetags/
| | `-- __init__.py
| |-- urls.py
| `-- views.py
|-- templates/
| `-- store/
`-- urls.py</pre>
<h4>Set up project</h4>
<ul>
<li>Create directory structure:
<pre>cd /srv
mkdir HandsOnCards
cd HandsOnCards
/srv/Django-1.0.2-final/django/bin/django-admin.py startproject handsoncards
cd handsoncards
mkdir bin
mkdir -p templates/store
./manage.py startapp store
mkdir -p store/templatetags
touch store/templatetags/__init__.py
</pre>
</li>
<li>Create log file
<pre>touch satchmo.log
chmod 666 satchmo.log</pre>
</li>
<li>Create the cache directory:
<pre>mkdir django_cache
chmod 777 django_cache</pre>
</li>
<li>Copy settings files:
<pre>cp /srv/satchmo-0.8/satchmo/local_settings-customize.py local_settings.py
cp /srv/satchmo-0.8/satchmo/settings-customize.py settings.py</pre>
</li>
<li>Set the Python path:
<pre>export PYTHONPATH=/srv/python-packages:/srv/HandsOnCards</pre>
</li>
<li>Copy static files:
<pre>python manage.py satchmo_copy_static
chmod 777 static/css
chmod 777 static/images
chmod 777 static/js</pre>
</li>
<li>Set the root URLconf. Edit <code>settings.py</code>:
<pre>ROOT_URLCONF = 'handsoncards.urls'</pre>
</li>
<li>Create <code>handsoncards/store/urls.py</code> to only contain the following:
<pre>from django.conf.urls.defaults import *
from satchmo.urls import urlpatterns</pre>
</li>
<li>Edit <code>handsoncards/urls.py</code> to only contain the following:
<pre>from django.conf.urls.defaults import *
from handsoncards.store.urls import urlpatterns</pre>
</li>
<li>Configure templates. Edit <code>local_settings.py</code>:
<pre>SATCHMO_DIRNAME = '/srv/python-packages/satchmo'
DIRNAME = os.path.abspath(os.path.dirname(__file__).decode('utf-8'))
TEMPLATE_DIRS = (
os.path.join(DIRNAME, "templates/store"),
os.path.join(DIRNAME, "templates"),
os.path.join(SATCHMO_DIRNAME, "templates"),
)</pre>
(I also commented out TEMPLATE_DIRS in settings.py since this replaces it.)
</li>
<li>Install the store app. Edit <code>settings.py</code>:
<pre>INSTALLED_APPS = (
[...]
'handsoncards.store', #should usually be last
)</pre>
</li>
<li>From my previous post, use the following database settings in
<code>settings.py</code>:
<pre>DATABASE_ENGINE = 'postgresql_psycopg2'
DATABASE_NAME = 'django_db'
DATABASE_USER = 'django_user'
DATABASE_PASSWORD = 'django_password'
DATABASE_HOST = ''
DATABASE_PORT = ''</pre>
</li>
</ul>
<h4>Configure misc. settings</h4>
<p>In settings.py:</p>
<pre>
LOCAL_DEV = False
ADMINS = (
(Sofeng', sofeng@myemail.com'),
)
TIME_ZONE = 'America/Los_Angeles'
#LANGUAGE_CODE = 'en-us.utf8'
MEDIA_ROOT = os.path.join(DIRNAME, 'static/')
MEDIA_URL = '/site_media/'
ADMIN_MEDIA_PREFIX = '/admin_media/'
SECRET_KEY = 'yoursecretkeyhere'
SATCHMO_SETTINGS = {
'SHOP_BASE' : '/shop',
[...]
}</pre>
<p>In local_settings.py:</p>
<pre>SITE_DOMAIN = "handsoncards.com"
SITE_NAME = "HandsOnCards.com"
CACHE_BACKEND = "file://" + DIRNAME + "/django_cache"</pre>
<h4>Test and install data</h4>
<ul>
<li>Set the Python path:
<pre>export PYTHONPATH=/srv/python-packages:/srv/HandsOnCards</pre>
</li>
<li>Test Satchmo setup:
<pre>python manage.py satchmo_check</pre>
Results:
<pre>Checking your satchmo configuration.
Using Django version 1.0.2 final
Using Satchmo version 0.8
Your configuration has no errors.</pre>
</li>
<li>Create database tables:
<pre>python manage.py syncdb</pre>
Go ahead and create a superuser
</li>
<li>Load country data:
<pre>python manage.py satchmo_load_l10n</pre>
</li>
<li>Load US tax table:
<pre>python manage.py satchmo_load_us_tax</pre>
</li>
</ul>
<h4>Set up httpd.conf and static media links</h4>
<ul>
<li>Create symbolic links in <code>/var/www</code>:
<pre>cd /var/www
ln -s /srv/python-packages/django/contrib/admin/media admin_media
ln -s /srv/HandsOnCards/handsoncards/static site_media</pre>
</li>
<li>Edit <code>/etc/apache2/httpd.conf</code>:
<pre><location "/">
SetHandler python-program
PythonHandler django.core.handlers.modpython
SetEnv DJANGO_SETTINGS_MODULE handsoncards.settings
PythonPath "['/srv/HandsOnCards', '/srv/python-packages'] + sys.path"
PythonDebug Off
</location>
<location "/site_media">
SetHandler None
</location>
<location "/admin_media">
SetHandler None
</location></pre>
</li>
<li>Restart apache:
<pre>/etc/init.d/apache2 restart</pre>
</li>
</ul>
<h4>View the store</h4>
<p>Navigate to <code>http://<em>[your domain or slice ip address]</em>/shop/</code>
in your browser. You should see an empty test store.
</p>
<h4>Create a Store Configuration</h4>
<ul>
<li>Go to <code>http://<em>[your domain or slice ip address]</em>/admin/</code> and log in.</li>
<li>Update the "Site". Click on "Sites". Click on "example.com" and set your domain.</li>
<li>Under the "Shop" heading, click on "Store Configurations". Click
"Add Store Configuration" and fill in the information.</li>
</ul>
<h4>Create categories and products</h4>
<ul>
<li>Create a Category:
<ul>
<li>Next to "Categories", click "Add"</li>
<li>Fill in the data</li>
</ul>
</li>
<li>Create a Product:
<ul>
<li>Next to "Products", click "Add"</li>
<li>Fill in the data</li>
</ul>
</li>
</ul>
<h4>Customize templates and CSS</h4>
<ul>
<li>Copy the desired templates from <code>/srv/satchmo-0.8/satchmo/templates</code>
to <code>/srv/HandsOnCards/handsoncards/templates/store</code> and edit them.
</li>
<li>To customize the style, edit <code>handsoncards/static/css/style.css</code>
</li>
</ul>
<h4>Redirect '/' to '/shop/'</h4>
<ul>
<li>Edit <code>handsoncards/urls.py</code> as follows:
<pre>from django.conf.urls.defaults import *
from handsoncards.store.urls import urlpatterns
urlpatterns += patterns(
'',
(r'^$', 'django.views.generic.simple.redirect_to', {'url': '/shop/'}),
)</pre>
</li>
</ul>
Card store project #2: Installing Satchmo, Django, PostgreSQL, and Apache on Ubuntu at Slicehost
2008-12-11T21:21:22-08:00https://www.saltycrane.com/blog/2008/12/card-store-project-2-installing-satchmo-django-postgresql-and-apache-ubuntu-slicehost/<p>As I mentioned in my
<a href="http://www.saltycrane.com/blog/2008/12/card-store-project-1-plans/">previous
post</a>, I'm planning to set up an e-commerce site using the
<a href="http://www.satchmoproject.com/">Satchmo</a> shopping cart framework.
Satchmo is built on the
<a href="http://www.satchmoproject.com/">Django</a> web framework which is
written in the
<a href="http://www.python.org/">Python</a> programming language. Satchmo
has a lot of <a href="http://www.satchmoproject.com/docs/svn/features.html">features</a>
built in which means it saves you a lot of work implementing them yourself.
Check out <a href="http://www.youtube.com/watch?v=d42a4g650Ws">this video
introduction to Satchmo</a> at this year's DjangoCon for more information.</p>
<p>After reading <a href="http://groups.google.com/group/satchmo-users/browse_thread/thread/b453d6453b6a28cf?pli=1">
this discussion</a> on the Satchmo mailing list, I decided to use
<a href="http://www.slicehost.com/">Slicehost</a> for hosting my site.
Their cheapest plan provides 256MB of RAM for $20/month.
Here are my notes on setting up Satchmo and Django with
<a href="http://www.postgresql.org/">PostgreSQL</a>,
<a href="http://httpd.apache.org/">Apache</a>,
and <a href="http://www.modpython.org/">mod_python</a> on
<a href="http://www.ubuntu.com/">Ubuntu</a> Intrepid at Slicehost.
It seems <a href="http://www.lighttpd.net/">lighttpd</a> and
<a href="http://nginx.net/">nginx</a> are popular lightweight alternatives to
Apache on VPS setups. I do not know too much about this, but may explore these
in the future.
</p>
<p>Note, I don't describe how to setup a Satchmo project below. Maybe I will
write another post about that later... For more information, see the
<a href="http://www.satchmoproject.com/docs/rel/0.8.1/new_installation.html">
Satchmo Installation documentation</a>. <em>Update 2008-12-12: I did write
another blog post-- see <a href="http://www.saltycrane.com/blog/2008/12/card-store-project-3-installing-satchmo-part-2/">
Installing Satchmo, part 2</a>.</em>
</p>
<h4>Setup Slicehost</h4>
<ul>
<li>Sign up for an account at <a href="http://www.slicehost.com/">http://www.slicehost.com/</a>
I chose Ubuntu Intrepid for my operating system and named my slice "toad".
</li>
<li>I followed the following two excellent Slicehost tutorials:
<a href="http://articles.slicehost.com/2008/4/25/ubuntu-hardy-setup-page-1">Ubuntu Setup Part 1</a>
and <a href="http://articles.slicehost.com/2008/4/25/ubuntu-hardy-setup-page-2">Part 2</a>.
</li>
<li>An important step is setting the system locale. Since I'm in the United States,
I used the following commands (run as root):
<pre>locale-gen en_US.UTF-8
update-locale LANG=en_US.UTF-8</pre>
</li>
</ul>
<h4>Install apache, mod_python, postgresql, and postgres python bindings</h4>
<ul>
<li>Run as root (or use sudo):
<pre>apt-get update
apt-get upgrade
apt-get dist-upgrade
apt-get install apache2 apache2-mpm-prefork
apt-get install libapache2-mod-python
apt-get install postgresql
apt-get install python-psycopg2</pre>
</li>
</ul>
<h4>Configure apache</h4>
<ul>
<li>I used the following Slicehost tutorials:
<a href="http://articles.slicehost.com/2008/4/25/ubuntu-hardy-installing-apache-and-php5">Install Apache</a>,
<a href="http://articles.slicehost.com/2008/4/28/ubuntu-hardy-apache-configuration-1">Configure Apache Part 1</a>,
<a href="http://articles.slicehost.com/2008/4/28/ubuntu-hardy-apache-configuration-2">Configure Apache Part 2</a>
</li>
<li>Of particular note, when installing apache, I got this warning:
<pre>apache2: Could not reliably determine the server's fully qualified domain name, using 127.0.0.1 for ServerName</pre>
Fix it by setting the ServerName in <code>/etc/apache2/apache2.conf</code>:
<pre>ServerName yourdomain.com
ServerTokens Prod</pre>
See the <a href="http://httpd.apache.org/docs/2.2/mod/core.html#servername">Apache
docs</a> for more information on ServerName.
</li>
</ul>
<h4>Install Django 1.0.2</h4>
<ul>
<li>Run the following as root:
<pre>cd /srv
wget http://www.djangoproject.com/download/1.0.2/tarball/
tar zxvf Django-1.0.2-final.tar.gz
mkdir python-packages
cd python-packages
ln -s ../Django-1.0.2-final/django</pre>
</li>
</ul>
<h4>Test Django (optional)</h4>
<ul>
<li>Run the following as root:
<pre>export PYTHONPATH=/srv/python-packages
/srv/Django-1.0.2-final/django/bin/django-admin.py startproject testdjango</pre>
</li>
<li>Edit <code>/etc/apache2/httpd.conf</code>:
<pre><location "/">
SetHandler python-program
PythonHandler django.core.handlers.modpython
SetEnv DJANGO_SETTINGS_MODULE testdjango.settings
PythonPath "['/srv', '/srv/python-packages'] + sys.path"
PythonDebug On
</location></pre>
</li>
<li>Restart apache:
<pre>/etc/init.d/apache2 restart</pre>
Navigate to your slice's IP address in your browser and you
should see the Django "It worked!" page.
</li>
</ul>
<h4>Install Satchmo</h4>
<p>Satchmo requires a number of external Python packages besides
Django. Unfortunately, there isn't one standard way of installing
Python packages. I decided to install most of them using Ubuntu's
APT package management and one of them using
<a href="http://peak.telecommunity.com/DevCenter/EasyInstall">easy_install</a>,
and one of them from source.
Alternatively, I could have installed most of the packages with
easy_install, and one of them with APT (and one of them from source).
Or, I could have done it another way... sigh.
</p>
<p>Note, I use the directory <code>/srv/python-packages</code> to
store symbolic links to my needed python packages. Then I add this
directory to my Python Path in the PYTHONPATH environment variable or in
the Apache <code>httpd.conf</code> file. See my post,
<a href="http://www.saltycrane.com/blog/2008/08/somewhere-your-python-path/">
Somewhere on your Python path</a> for more information.
</p>
<ul>
<li>Install Satchmo prerequisites. Run the following commands as root:
<br><b>Install APT packages:</b>
<pre>apt-get update
apt-get upgrade
apt-get install python-crypto
apt-get install python-yaml
apt-get install python-imaging
apt-get install python-reportlab
apt-get install python-trml2pdf</pre>
<br><b>Install django-registration using easy_install:</b>
<pre>apt-get install python-setuptools python-dev build-essential
easy_install django-registration</pre>
<br><b>Install comment_utils source:</b>
<pre>apt-get install subversion
mkdir -p /srv/python-packages/dist
cd /srv/python-packages/dist
svn co http://django-comment-utils.googlecode.com/svn/trunk/comment_utils/
mv comment_utils comment_utils_rev92
cd /srv/python-packages
ln -s dist/comment_utils_rev92 comment_utils</pre>
</li>
<li>Install Satchmo 0.8. Run the following as root: <em>Update 2008-12-12:
I changed this to use release 0.8 instead of the SVN trunk version.</em>
<pre>cd /srv
wget http://www.satchmoproject.com/snapshots/satchmo-0.8.tgz
tar zxvf satchmo-0.8.tgz
cd /srv/python-packages
ln -s ../satchmo-0.8/satchmo</pre>
To install the SVN trunk version instead,
<pre>cd /srv
svn co svn://satchmoproject.com/satchmo/trunk
mv trunk satchmo_revXXXX
cd /srv/python-packages
ln -s ../satchmo_revXXXX/satchmo</pre>
</li>
</ul>
<h4 id="postgres">Setup PostgreSQL</h4>
<p>I followed the
<a href="http://www.punteney.com/writes/setting-django-slicehost-ubuntu-hardy-postgres-apa/">instructions
at Punteney.com</a> for setting up Django and Postgres on Slicehost.
</p>
<ul>
<li>If you didn't already install Postgres above, run the following as root:
<pre>apt-get install postgresql
apt-get install python-psycopg2</pre>
</li>
<li>Change the password for the "postgres" Unix user. (run as root or use sudo)
<pre>passwd -d postgres
su postgres -c passwd</pre>
</li>
<li>Change password for "postgres" user in the database.
(From Punteney's article, "It's convenient
to have the two passwords match, but not required.")
<pre>su postgres -c psql template1</pre>
<pre>ALTER USER postgres WITH PASSWORD 'postgres_user_password';
template1=\q</pre>
</li>
<li>Create django user:
<pre>su postgres
createuser -P django_user</pre>
Punteney says to answer no to all here.
Remember the password to put in Django settings.py file. (For this
example, I set the password to <code>my_password</code>.)
</li>
<li>Create a database. Still <code>su</code>'ed as the "postgres" user:
<pre>psql template1</pre>
<pre>CREATE DATABASE django_db OWNER django_user ENCODING 'UTF8';
\q</pre>
<pre>exit</pre>
(exit from postgres su)
</li>
<li>Configure access to the database. Edit
<code>/etc/postgresql/8.3/main/pg_hba.conf</code>:
<pre>local all postgres ident sameuser
local django_db django_user md5</pre>
</li>
<li>Restart the postgres server:
<pre>/etc/init.d/postgresql-8.3 restart</pre>
</li>
<li>In your Django project, use the following database settings in
<code>settings.py</code>:
<pre>DATABASE_ENGINE = 'postgresql_psycopg2'
DATABASE_NAME = 'django_db'
DATABASE_USER = 'django_user'
DATABASE_PASSWORD = 'my_password'
DATABASE_HOST = ''
DATABASE_PORT = ''</pre>
</li>
<li>Run <code>syncdb</code>:
<pre>cd /srv/yourproject
python manage.py syncdb</pre>
</li>
</ul>
<h4>Setup a domain name</h4>
<ul>
<li>Register for a domain name. Google for "domain registration" for
options.
</li>
<li>Follow the
<a href="http://articles.slicehost.com/2007/10/24/creating-dns-records">
Slicehost directions</a> for creating DNS records. This includes
setting the nameservers at your domain registration service to use
Slicehost's nameservers (ns1.slicehost.net, ns2.slicehost.net, ns3.slicehost.net).
</li>
</ul>
Card store project #1: Plans
2008-12-08T11:09:35-08:00https://www.saltycrane.com/blog/2008/12/card-store-project-1-plans/<p>I'm planning to start an online greeting card / stationary store with my wife.
My wife (and possibly my sister
and other friends) will create designs for the cards, while
I'll run the website and manage the business. I'll admit, I don't know
the first thing about running a business, but, for a number of reasons, I still
want to try. If you have any advice on running an online business, I'd appreciate
any feedback. Here are some of my plans for the store. I've already started on some
of them and will write more details as I complete them.
</p>
<h4>General plans</h4>
<p>My general plan is to have my wife, and possibly other artists, draw
card designs using colored pencils, ink, and other media. I'll scan
the designs, lay out the cards, and print them. We had thought about doing
custom, hand-made cards for each order, but my wife doesn't have a lot of
time, and I didn't want to burden her with making tens of millions of cards
when our store hits the big time tomorrow.
I plan to make the cards
on-demand, with quick turn around times by automating as much of the process
as possible. In the beginning we'll do things manually, because I don't
expect many orders. Then later, I'll add <a href="http://www.python.org/">Python</a>
scripts,
<a href ="http://twistedmatrix.com/trac/">Twisted</a>
Perspective Broker communication, and custom robotic cutting, folding, and
mail delivery. Finally, I plan to <a href="http://en.wikipedia.org/wiki/Pinky_and_the_Brain">
take over the world</a>.
</p>
<h4>Desktop publishing plans</h4>
<p>A degree in Art, or Graphic Design would probably
be a whole lot more useful than my degree in Engineering. If I did have one of those
degrees, I would probably be doing most of my work on a Mac with Photoshop,
Illustrator, InDesign, etc. But, since I'm a geeky engineer and programmer,
I'm going to use Linux, the <a href="http://www.gimp.org/">Gimp</a>,
and deal with all the hardware/software
incompatibilities that come with it. Besides Gimp,
I plan to use <a href="http://www.scribus.net/">Scribus</a>, a pretty cool
open-source desktop publishing program. For printing, I've already purchased
a <a href="http://www.office.xerox.com/printers/color-printers/phaser-8560/enus.html">
Xerox Phaser 8560DN</a> solid-ink printer. I've done some preliminary
printing, and there has been some pain, as my wife can attest, but overall
it is a pretty cool printer that works well with Linux. The biggest hurdle,
I think, will be figuring out the ICC profiles / color management stuff.
I'll post my Linux Phaser 8560 setup notes when I have everything in order.
</p>
<h4>Business-related plans</h4>
<p>I plan to run the business as a <a href="http://en.wikipedia.org/wiki/Sole_proprietorship">
sole proprietorship</a> since it is the simplest of the
<a href="http://www.sba.gov/smallbusinessplanner/start/chooseastructure/START_FORMS_OWNERSHIP.html">
business structure options</a>. As far as I can tell from preliminary reading,
I will need to get a city business license, a sales tax license, a state
employer identification number, and probably a
<a href="http://entrepreneurs.about.com/od/businessstructure/a/doingbusinessas.htm">
ficticious business name</a>. For more information, see the
<a href="http://www.sba.gov/smallbusinessplanner/index.html">
U.S. Small Business Administration guide</a>.
</p>
<h4>Website plans</h4>
<p>Regarding website plans, I need to do a lot of work to learn the front end
web development such as design, CSS, and Javascript. On the back end, I have
a little more experience, though, again, I still have a lot to learn. Here are
my website technical plans.
</p>
<ul>
<li><a href="http://www.satchmoproject.com/">Satchmo</a>:
Satchmo is an open source e-commerce framework built
on Python and
<a href="http://www.djangoproject.com/">Django</a>.
It is <a href="http://www.satchmoproject.com/blog/">actively
developed</a>, has a number of
<a href="http://www.satchmoproject.com/docs/svn/features.html">features</a>,
and, since it is written in Python/Django, is easily hackable should I have
custom needs.</li>
<li><a href="http://www.slicehost.com/">Slicehost</a> hosting: I originally thought
I could use <a href="http://www.webfaction.com/">Webfaction</a> shared hosting since
it has served me well for this blog. However, after reading
<a href="http://groups.google.com/group/satchmo-users/browse_thread/thread/b453d6453b6a28cf">
this discussion on Satchmo hosting</a>, I realized I needed more RAM and
decided to go with VPS hosting at Slicehost.</li>
<li>I plan
to use <a href="http://aws.amazon.com/s3/">Amazon S3</a> for media storage.
I gained experience with this service at work. Also, after reading
Adrian Holovaty's (somewhat dated) <a href="http://holovaty.com/blog/archive/2006/04/07/0927">
post on using Django and S3 for media files</a>, I implemented S3 storage
for this blog. With the beta release of <a href="http://aws.amazon.com/cloudfront/">
Amazon CloudFront</a>, performance should be even better.
</li>
<li>Other technologies: I'll stick with <a href="http://www.ubuntu.com/">Ubuntu</a>
for the
server OS. I'll start with <a href="http://www.apache.org/">Apache</a> for the
web server, since it recommended by Django. I may explore other options if
memory becomes a problem. Though I have slightly more experience with
<a href="http://www.mysql.com/">MySQL</a> from work, I plan to use
<a href="http://www.postgresql.org/">PostgreSQL</a> for the database,
again, since it is
recommended by Django and seems to be a better designed database. I don't know
very much about caching, but I plan to look into
<a href="http://www.danga.com/memcached/">memcached</a>,
<a href="http://nginx.net/">nginx</a>, and
<a href="http://www.squid-cache.org/">squid-cache</a>.
</li>
</ul>
<br>
<p>Well, those are my plans. I am starting off bright-eyed. Hopefully, the trials
of the road ahead will not wreck my spirit. Again, feedback is appreciated! Thanks.
</p>