In part 1 of this series, we saw how to pull Docker images from Docker Hub and launch Docker containers. We interacted with a running Docker container by running some bash commands, in this tutorial we will see how to use Dockerfile to automate image building for quicker deployment of applications in a container.

Dockerfile is a text file containing a set of instructions or commands in order to build a Docker image.


1. Complete the tutorial on part 1 before proceeding. You will need a Docker engine running and the latest official Ubuntu Docker images locally hosted.

2. Create directories

$mkdir ~/docker-flask 
$cd ~/docker-flask

3. Add Dockerfile : ~/docker-flask/Dockerfile
The commands below will be used to create the Docker image. It will pull the latest Ubuntu official Docker image as a first layer or base. Then it will resynchronize the apt package index files from their sources.

A /flask directory will be created in the image, followed by installing Flask and running our Flask app, which we will write in next step.

FROM ubuntu:latest
RUN apt-get update && apt-get install -y python-pip python-dev
COPY . /flask
WORKDIR /flask
RUN pip install -r requirements.txt
ENTRYPOINT ["python"]
CMD [""]

4. Write flask app : ~/docker-flask/
Let us write a practical app, rather than just printing hello world. The flask app will return the user agent information of the visitor if the index page is visited.

We will also have a URL under /status/ followed by a valid HTTP status code. Given this HTTP status code by the visitor, the flask web server will generate the same HTTP status code header. For instance, if the user visits http://localhost/status/502, the flask server will respond with ‘502 BAD GATEWAY’ HTTP header.

Let us write it under ~/docker-flask/

from flask import Flask
from flask import request, jsonify

app = Flask(__name__)

def user_agent():
    user_agent = request.headers.get('User-Agent')
    return 'Your browser is %s.' % user_agent

def get_status(httpcode):
    httpcode = int(httpcode)
    if httpcode < 100 or httpcode >= 600:
        return jsonify({'Status': 'Invalid HTTP status code'})
    elif httpcode >= 100 and httpcode < 500:
        return jsonify({'Status': 'UP'}) , httpcode
        return jsonify({'Status': 'DOWN'}) , httpcode

if __name__ == '__main__':, host='', port=80)

5. requirements.txt : ~/docker-flask/requirements.txt

cat ~/docker-flask/requirements.txt

By now, your directory structure should look similar to this –

daniel@lindell:~/docker-flask$ pwd

daniel@lindell:~/docker-flask$ ls  Dockerfile  requirements.txt

Time to build the Docker image –

sudo docker build -t flaskweb:latest .

This will execute the series of commands under Dockerfile. If successful, you will end up with a Docker image named flaskweb and tagged latest –

root@lindell:~# docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
flaskweb            latest              6b45443b6380        22 minutes ago      440.4 MB
ubuntu              latest              104bec311bcd        2 weeks ago         129 MB

If you encounter any errors, validate you don’t have any syntax errors on Dockerfile.

It is time to run the container –

daniel@lindell:~/docker-flask$ sudo docker run -d -p 80:80 flaskweb

daniel@lindell:~/docker-flask$ sudo docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED              STATUS              PORTS                NAMES
d9af9a1c92bf        flaskweb            "python"     About a minute ago   Up About a minute>80/tcp   drunk_mcnulty

In this case, both the host and container will be listening on port 80, feel free to modify this according to your setup.

Test it, we will use httpie to query the web server, if you don’t have httpie installed, you can use ‘curl -I’ to get the full header –

daniel@lindell:~/blog/docker-flask$ http http://localhost/
HTTP/1.0 200 OK
Content-Length: 29
Content-Type: text/html; charset=utf-8
Date: Fri, 30 Dec 2016 14:35:53 GMT
Server: Werkzeug/0.11.13 Python/2.7.12

Your browser is HTTPie/0.9.2.

daniel@lindell:~/blog/docker-flask$ http http://localhost/status/404
Content-Length: 21
Content-Type: application/json
Date: Fri, 30 Dec 2016 14:35:56 GMT
Server: Werkzeug/0.11.13 Python/2.7.12

    "Status": "UP"

daniel@lindell:~/blog/docker-flask$ http http://localhost/status/502
Content-Length: 23
Content-Type: application/json
Date: Fri, 30 Dec 2016 14:35:58 GMT
Server: Werkzeug/0.11.13 Python/2.7.12

    "Status": "DOWN"

Full clean up – if you want to start all over again or want to delete the container and images we have created, i have outlined the steps below. The first step is to stop the running container using ‘docker stop’ command, pass it the first few digits of the container ID.

Once the container is stopped, use ‘docker rm’ to delete the container. At this point, we can proceed with deleting the image as the image is not attached to any running container. Use ‘docker rmi’ to delete the image. We will keep the base Ubuntu image for future use.

daniel@lindell:/tmp$ sudo docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS                NAMES
d9af9a1c92bf        flaskweb            "python"     12 minutes ago      Up 12 minutes>80/tcp   drunk_mcnulty

daniel@lindell:/tmp$ sudo docker stop d9a

daniel@lindell:/tmp$ sudo docker rm d9a

daniel@lindell:/tmp$ sudo docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES

daniel@lindell:/tmp$ sudo docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
flaskweb            latest              6b45443b6380        39 minutes ago      440.4 MB
ubuntu              latest              104bec311bcd        2 weeks ago         129 MB

daniel@lindell:/tmp$ sudo docker rmi 6b45
Untagged: flaskweb:latest
Deleted: sha256:6b45443b63805583f41fbf60aaf5cf746b871fdcfa8fe1c6d5adfb52870e7c89
Deleted: sha256:02062a8ea251d993f54e15f9e5654e40894449430acd045476000cd9ebbdf459
Deleted: sha256:fa2439cd5bc8a53152877c1dc3b12a60ab808bcfe5078549ea5e945f462330da
Deleted: sha256:3bac38b223d80a4db6c4283fd56275fe05ceeab6a1dfd81871aa14c6cda387df
Deleted: sha256:d97357dc5d7454e3b7757f2c348323c84d1902dd806792c53d1fd0ca7813b091
Deleted: sha256:b55dd5bd3326ec4657dc389f4aae69c34a7ba222872f7b868eb8de69d7f69dab
Deleted: sha256:eab59ae84eb136339d08fbacd2905a1ee80a0c875e8e14a4d5184fac30445714
Deleted: sha256:588253a9066c49786fcd0121353e7f0f2cea05cebbc6b9cef67f0c823d23dce8
Deleted: sha256:fe9f27a1cb9165531a1f5149c16ebcd522422e4ac2610035bbbcada7fd0b7551
Deleted: sha256:18ca1bc40895f6f97cae28fa5707bde537ac27023762303f98912c11549431ae

daniel@lindell:/tmp$ sudo docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
ubuntu              latest              104bec311bcd        2 weeks ago         129 MB

References –

The scheduling format for the Linux scheduling daemon cron are not easy to remember, especially if you don’t work with cron that frequently. The first reaction for most Linux sys admins when they can’t remember the ordering of fields is to type ‘man crontab’, and unfortunately this man page section does not contain the schedule format information. If you are like me, you will immediately start Googling it.

What is the best way to locate the man page for crontab scheduling format then? For one thing, you can search the man page for the key work ‘crontab’ using the command below –

daniel@linubuvma:/tmp$ man -k crontab
anacrontab (5)       - configuration file for anacron
crontab (1)          - maintain crontab files for individual users (Vixie Cron)
crontab (5)          - tables for driving cron

You see, there are two sections for crontab – section 1 describes the command usage and section 5 shows the tables we are looking for. If you are familiar with how man page section numbers are assigned, you would have immediately jumped to section 5 of the man page for crontab –

1. General commands
2. System calls
3. C library functions
4. Special files (usually devices, those found in /dev) and drivers
5. File formats and conventions
6. Games and screensavers
7. Miscellanea
8. System administration commands and daemons

Short answer to how do i see the crontab schedule format is – run

 man 5 crontab 

Per the man page, the time and date fields in order are –

field allowed values
----- --------------
minute 0-59
hour 0-23
day of month 1-31
month 1-12 (or names, see below)
day of week 0-7 (0 or 7 is Sun, or use names)

One of the most commonly used Linux system administration tools is chown, which is part of the coreutils package. It is used to change the user and/or group ownership of a given file or directory. Something to be aware of this tool is, it doesn’t change the ownership of symbolic links, as shown below –

root@linubuvma:/tmp# touch test
root@linubuvma:/tmp# ls -l test
-rw-r--r-- 1 root root 12 Dec 20 08:01 test
root@linubuvma:/tmp# ln -s test sltest
root@linubuvma:/tmp# ls -l sltest
lrwxrwxrwx 1 root root 4 Dec 20 08:01 sltest -> test
root@linubuvma:/tmp# chown daniel:daniel sltest
root@linubuvma:/tmp# ls -l sltest
lrwxrwxrwx 1 root root 4 Dec 20 08:01 sltest -> test

The reason this doesn’t work is in the man page for chown – symbolic links named by arguments are silently left unchanged unless -h is used.” By simply running chown on symbolic link without ‘-h’ option, you are changing the ownership of the target. The ‘-h’ option affects symbolic links instead of any referenced file.

root@linubuvma:/tmp# chown -h daniel:daniel sltest

root@linubuvma:/tmp# ls -l sltest
lrwxrwxrwx 1 daniel daniel 4 Dec 20 08:01 sltest -> test

Though not portable, in some distros

 chown -R 

will recursively change the owernship of all files, including symbolic link files and directories. In my case, ‘chown -R /path/to/file’ works for GNU chown which is part of the ‘GNU coreutils 8.21’ package on Ubuntu 14.04.

How to record your ssh session using screen.

Per the man page – “Screen is a full-screen window manager that multiplexes a physical terminal between several processes (typically interactive shells)”.
Screen is most commonly used to create multiple sessions to remote hosts within a single terminal window or even run multiple commands locally without leaving your shell terminal. For instance, you could be tailing the log file in one session, then run a long process, then ssh into other machine etc. all within a single window.

Screen is the go to tool when setting up a remote connection, such as ssh, and you want to continue your work at any time or from any other host without worrying of a dropped connection.

In this post, I will show you how you can record your bash session.

Installation –

yum install screen        (Debian/Ubuntu)
apt-get install screen    (Redhat/CentOS)

My local environment and the remote host I am sshing to –

daniel@linubuvma:/tmp$ screen -v
Screen version 4.01.00devel (GNU) 2-May-06
daniel@linubuvma:/tmp$ uname -r
daniel@linubuvma:/tmp$ cat /etc/issue
Ubuntu 14.04.5 LTS \n \l

daniel@linubuvma:/tmp$ ssh ns2 'uname -r ; cat /etc/issue'
CentOS release 6.8 (Final)
Kernel \r on an \m

The ‘-L’ option of screen is used to record your session, the session log is automatically saved in a file named ‘screenlog.n’ in your current directory.

daniel@linubuvma:/tmp$ ls
config-err-hbzs5e  one          ssh-4yheApHRgMBF  ssh-RK7GpeFuzUB8  VMwareDnD    vmware-root-2347660412
gpg-kZux7q         screenlog.0  ssh-BBblvGtb5284  vmware-daniel     vmware-root
daniel@linubuvma:/tmp$ free -m
             total       used       free     shared    buffers     cached
Mem:          3946       2489       1457          6        547       1031
-/+ buffers/cache:        911       3035
Swap:         4092          0       4092
daniel@linubuvma:/tmp$ exit
[screen is terminating]

The whole bash session will be logged in screenlog.0 in this case –

daniel@linubuvma:/tmp$ cat screenlog.0 
daniel@linubuvma:/tmp$ ls
config-err-hbzs5e  one          ssh-4yheApHRgMBF  ssh-RK7GpeFuzUB8  VMwareDnD    vmware-root-2347660412
gpg-kZux7q         screenlog.0  ssh-BBblvGtb5284  vmware-daniel     vmware-root
daniel@linubuvma:/tmp$ free -m
             total       used       free     shared    buffers     cached
Mem:          3946       2489       1457          6        547       1031
-/+ buffers/cache:        911       3035
Swap:         4092          0       4092
daniel@linubuvma:/tmp$ exit

Recording your session of an ssh connection to a remote host is also similar, with ‘-L’ option followed by the command to ssh to remote host.
Option -fn (with no flow-control)
Option -t (title bar name) in this case ‘practice’.

daniel@linubuvma:/tmp$ screen -fn -t practice -L  ssh ns2
Last login: Tue Dec 27 09:46:10 2016 from

[daniel@kauai ~]$ hostname -f
[daniel@kauai ~]$ uptime
 10:08:18 up 18 days, 10:02, 14 users,  load average: 0.19, 0.49, 0.64
[daniel@kauai ~]$ exit
[screen is terminating]

daniel@linubuvma:/tmp$ cat screenlog.0 
Last login: Tue Dec 27 09:46:10 2016 from
[daniel@kauai ~]$ hostname -f
[daniel@kauai ~]$ uptime
 10:08:18 up 18 days, 10:02, 14 users,  load average: 0.19, 0.49, 0.64
[daniel@kauai ~]$ exit
Connection to ns2 closed.

Additional resources –

Randomly ordering files in a directory with python

I have a playlist file which contains audio files to play. The audio player unfortunately plays the music files in a sequential order, in whatever order they are listed in the playlist file. So occasionally I have to regenerate the playlist file to randomize the audio files order. Here is a simple script that I had to write for this purpose, the core component is the random.shuffle(list) python function –

Create script file as –

#!/usr/bin/env python

import os
import random
import sys


if len(sys.argv) != 2:
  print "Usage:", sys.argv[0], "/path/directory"
  if os.path.isdir(dir_name):
    for file_name in os.listdir(dir_name):
    print "Directory", dir_name, "does not exist"
# shuffle list
for item in music_files:
  print os.path.join(dir_name,item)

Run the script by providing a path to a directory with files. Each iteration should list the files in the directory in a different order.
Note – the script does not recurse into the directories, it can be easily modified with os.walk if necessary.

root@svm1010:/home/daniel/scripts# python /opt/iotop/iotop

root@svm1010:/home/daniel/scripts# python /opt/iotop/iotop

root@svm1010:/home/daniel/scripts# python /opt/iotop/iotop

Reference –

Ngrep is a very user friendly packet sniffer, basically the “grep” equivalent at the network layer.

Here is a quick way of figuring out the http connections your browser is making even if you are browsing to a secure site, make sure that is the only site you are visiting as the command will capture all port 80 connections.

Installation –

apt-get install ngrep

Let us redirect all traffic ngrep captured to a file –

ngrep -d any -W byline port 80 | tee  /tmp/net_output

Now visit a secure site, say, you will see nicely formated output

root@lindell:~# ngrep -d any -W byline port 80 | tee  /tmp/output
interface: any
filter: (ip or ip6) and ( port 80 )
T -> [AP]
POST / HTTP/1.1.
User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:50.0) Gecko/20100101 Firefox/50.0.
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8.
Accept-Language: en-US,en;q=0.5.
Accept-Encoding: gzip, deflate.
Content-Length: 83.
Content-Type: application/ocsp-request.
Connection: keep-alive.

From here, you can parse the /tmp/output file.

Similarly, you can parse the output file for the type of web server your favorite sites are using. Keep the ngrep command running, and visit all your favorite sites. Note, this works for http only, as https traffic is encrypted, for https only destination IP and port are shown.

In this case, I searched for the ‘Server:’ field in the HTTP response header from the web server. Apparently, nginx seems to be most popular, it is also interesting to see that AmazonS3 storage being used for hosting static content –

root@lindell:~# awk '/Server:/ {print $2}' /tmp/output |sort | uniq -c |sort -nr
    155 nginx.
     40 Apache.
     36 Apache-Coyote/1.1.
     20 Apache/2.2.3
     14 nginx/1.8.1.
      7 AmazonS3.
      6 Akamai
      5 ECS
      5 cloudflare-nginx.
      4 Omniture
      4 ESF.
      3 sffe.
      3 nginx/1.10.2.
      2 Microsoft-IIS/7.5.
      2 gws.
      2 AkamaiGHost.
      1 WildFly/8.
      1 Varnish.
      1 openresty.
      1 NetDNA-cache/2.2.
      1 Cowboy.
      1 ATS.
      1 Apache/2.2.14

References –

How to locate broadband Internet service providers in your area.

The FCC keeps a database of national broadband providers and it is publicly accessible at Just enter your full address or Zip code, and it will the broadband providers in your area as well as the advertised speed. One caveat is the data was last updated on June 2014, thus you might get latest information.

I checked the database for an area which had Google Fiber for the last 9 or 10 months, and it didn’t show Google Fiber as available in that area. The database has Google Fiber Inc. as a provider listed though.

If you want to check if Google Fiber is available or coming soon to your area check

Once nice thing about the National broadband Map is the open standards API they made available to the public. It is well documented and very easy to pull data from programmatically. The API also gives you access to Census data and demographic information.

Note – most of the queries require the FIPS state and/or county codes (Federal Information Processing Standard state code). For instance, for New York state, the FIPS code is 36. Any county within a state will have FIPS county code of state FIPS code + county FIPS code. Bronx county’s (FIPS 005) full code would be 36005, for instance.

Here is a simple python script on how to interact with the API, will use Bronx county and/or NY as an example.

Let us get the overall broadband ranking within New York state –

import requests
for item in r:
    print item.get('rank'), item.get('geographyName')

Output based on ranking would look like this –
1 Franklin
2 Cattaraugus
3 Allegany
4 Schoharie
5 Otsego
6 Lewis
7 Washington
8 Hamilton
9 Yates
10 Delaware
11 Steuben
12 Wyoming
13 Cayuga
14 Jefferson
15 Herkimer
16 Schuyler
17 Essex
18 Seneca
19 St. Lawrence
20 Clinton
21 Montgomery
22 Chautauqua
23 Wayne
24 Columbia
25 Greene
26 Tioga
27 Livingston
28 Tompkins
29 Rensselaer
30 Chemung
31 Genesee
32 Cortland
33 Oswego
34 Sullivan
35 Albany
36 Oneida
37 Chenango
38 Orleans
39 Fulton
40 Madison
41 Niagara
42 Ontario
43 Warren
44 Schenectady
45 Ulster
46 Erie
47 Putnam
48 Onondaga
49 Saratoga
50 Broome
51 Suffolk
52 Monroe
53 Kings
54 Queens
55 New York
56 Bronx
57 Nassau
58 Westchester
59 Richmond
60 Orange
61 Rockland
62 Dutchess

Bronx county is ranked 56 out of 62, and the data for Bronx would be –

for item in r:
    if item.get('geographyId') == '36005':
        print item

{u'anyWireline': 1.0,
 u'anyWirelineError': 0.0,
 u'downloadSpeedGreaterThan3000k': 1.0,
 u'downloadSpeedGreaterThan3000kError': 0.0,
 u'geographyId': u'36005',
 u'geographyName': u'Bronx',
 u'myAreaIndicator': False,
 u'population': 1482311,
 u'providerGreaterThan3': 1.0,
 u'rank': 56,
 u'stateFips': u'36',
 u'wirelineProviderEquals0': 0.0}

There is lots more you can do with the data, feel free to dig further.

In some cases, you might want to block all users from logging in to the system or just after you login, you want to prevent everyone else from connecting to the server. During server maintenance, this could be helpful or there are use cases where only one actively logged in user has to do some work if the username is a shared account.

Solution – create the /etc/nologin file, and put the text notice as the body of the file. If a user attempts to log in to a system where this file exists, the contents of the nologin file is displayed, and the user login is terminated.

[root@kauai ~]# echo 'System is under maintenance till Dec. 24, 2PM EST.' > /etc/nologin

Now try to login to the server under non super user –

daniel@linubuvma:~$ ssh ns2
System is under maintenance till Dec. 24, 2PM EST.
Connection closed by

If your ssh configuration allows it, root user can login to the server though, the root user will still be greeted with the contents of /etc/nologin file though –

daniel@linubuvma:~$ ssh root@ns2
root@ns2's password:
System is under maintenance till Dec. 24, 2PM EST.
Last login: Sat Dec 12 01:11:35 2015 from
[root@kauai ~]# 

Reference –

During user login, a Linux box might show message of the day(motd), new email, or package updates information. This is particularly common in Ubuntu boxes. In some cases, you want to prevent all these messages from being displayed as it could be delaying your login for instance.

Solution – Create a file named .hushlogin in the user’s home directory.

A typical login to an Ubuntu box might look like this –

[daniel@kauai etc]$ ssh practice
daniel@practice's password: 
Welcome to Ubuntu 14.04.1 LTS (GNU/Linux 3.13.0-39-generic x86_64)

 * Documentation:

  System information as of Sat Jan 10 11:37:24 EST 2015

  System load:  0.0                Processes:           290
  Usage of /:   46.1% of 45.15GB   Users logged in:     1
  Memory usage: 13%                IP address for eth0:
  Swap usage:   0%

  Graph this data and manage this system at:

168 packages can be updated.
63 updates are security updates.

You have new mail.
Last login: Sat Jan 10 11:37:26 2015 from linux.local

To suppress all this information, create a .hushlogin file in the users home directory and log out and login back –

daniel@linubuvma:~$ touch ~/.hushlogin

daniel@linubuvma:~$ exit
Connection to practice closed.

[daniel@kauai etc]$ ssh practice
daniel@practice's password: 


How to be financially independent.

For clarity I am going to define financial independence by what it is not first. There are different meanings of financial independence out there, for the sake of this post I will narrow down the scope. It is not objective definition but hopefully you get the gist of it once you finish reading this article. Financial independence does not mean full retirement where you spend the rest of your life travelling or relaxing in some pristine beaches. Neither does it mean just being out of debt or having a few months of emergency fund.

You might consider yourself to be financially independence once you get to the point where you no longer have to panic if you were to lose your job right now, or you don’t lose sleep about upcoming day to day expenses. You will reach it once you have your basic needs easily met from what your assets generate. It is possible that you might have to work, but you will no longer pick jobs on which one pays more, or keep on staying in a toxic work environment. You will have plenty of time to focus on your hobbies and personal development, and might actually end up doing creative work from your imagination, not because someone told you to do so under a deadline. With some of the tips given below, it is achievable and it is worth it.

Rule of thumb – Multiply your annual expenses by 25. If that number is closer to or better yet less than your net worth, then consider yourself financially independent. Or if 4% (7% market gain – 3% inflation) of your net worth is closer to (or less higher than) how much you need per year, you are financially independent.

1. Pay all your debt and stay out of it – financial independence and debt do not go together. There are exceptions of course, where the debt is considered as an investment such as going to medical school. No credit card debt, no mortgage for a house you can’t afford, no auto loans, no student debt for a worthless degree. Make sure to pay the ones with high interest rates, especially credit card debt. Pay it all and stay out of it! Good debt vs. bad debt.

2. Live below your means – this is no-brainer. If you are spending more than what you are bringing it, then say bye bye to financial independence. Frugal living is not death sentence.

3. Cut expenses – go check all your expenses and eliminate the ones you don’t need. Pay special attention to multiple small fee subscriptions or recurring ones. This is death by thousand cuts, they add up. Think of all the expenses that you can cut.

4. Stay healthy – Health care is expensive, I have read somewhere that sixty percent of personal bankruptcies are due to health care related expenses. Besides, you won’t enjoy your free time if you are not healthy. So take good care of your health – eat healthy food, exercise, sleep well and don’t stress out. Be healthy.

5. Have a marketable skill – one way to earn more money is to be skillful in an area which is in high demand. Finding a job will be easier and the pay would be better. This does not necessarily require college degree, there are many professions with plenty of self taught professionals. Continuously update your skills, all you need is access to the Internet and by this I don’t mean Facebook! Fastest growing occupations.

6. Take advantage of 401k, Roth IRA or traditional IRA – If your employer provides 401k, contribute to the maximum allowed. Similarly with Roth IRA, you can do this one on your own. Create an account with Vanguard or Fidelity, and start investing in low cost mutual funds, the risk is minimal. If you are adventurous you can invest in real estate, peer to peer lending etc. How to open an IRA.

7. Build passive sources of income – Put your money where it works for you, it should generate some dividend or interest. Don’t stash it in a zero interest saving account, eventually inflation alone will wipe it out. So be thoughtful of where you put your money. CD rates.

8. Have multiple income streams – Diversifying is good, make sure you don’t put all your eggs in one basket. If you have any free time – besides what you set aside for fun, family time, socializing etc. – you can use it to do freelancing, tutoring, selling stuff online and more. The passive sources of income mentioned above can fall into this category as well. Some tips on how to generate extra income.

9. Don’t give in to peer or social pressure – Stick to your plan even if your immediate circle of friends or family members have a different life style and pressure to make decisions that hinder your financial independence journely. This means no new car every two or three years, in fact buying a new car is a bad financial decision in most cases. Save for a car and pay cash for a slightly used car. Avoid keeping Up With The Joneses’, it might actually keep you broke.

10. Give – this might sound non sequitur, but remember the objective of financial independence is not the accumulation of money for the sake of money. Use money as a means to an end – in this case to personal freedom, free time, accomplishing your dreams – not hoarding money. Giving releases us from the power of money.Help the less fortunate folks. Volunteer.You are not an island. No one did it on their own, we all had someone in the past who steered us to the right direction be it a parent, relative, teacher, stranger etc. Be part of something by giving your money, time etc.

Note – these are rules of thumb and generalizations. If you have a justifiable reason and you surely know what you are doing, you can break some of these rules.

Some other sites to visit, I don’t fully agree with all the opinions on these sites, but I found them helpful in one way or another –
Mr. Money Mustache
Retire by 40
Can I retire yet.
Financial samurai
The Millionaire next door.