diff --git a/data/rail.png b/data/rail.png
new file mode 100644
index 0000000000000000000000000000000000000000..18176d9f3ca96ec9d6e4f6d41ab7a370aa93afeb
Binary files /dev/null and b/data/rail.png differ
diff --git a/data/rail_crossing.png b/data/rail_crossing.png
new file mode 100644
index 0000000000000000000000000000000000000000..98464057704a84ab9dfeac63f13e9b1af8d6f1ae
Binary files /dev/null and b/data/rail_crossing.png differ
diff --git a/data/rail_curved.png b/data/rail_curved.png
new file mode 100644
index 0000000000000000000000000000000000000000..62afa3d2b81c4dc6f85275ea6de89ffb43a1be8b
Binary files /dev/null and b/data/rail_curved.png differ
diff --git a/data/rail_t_junction.png b/data/rail_t_junction.png
new file mode 100644
index 0000000000000000000000000000000000000000..9985f63cd49ce29f636e72fe3b168fe1b6bad236
Binary files /dev/null and b/data/rail_t_junction.png differ
diff --git a/src/mapblock.cpp b/src/mapblock.cpp
index c6b82634db21bf6bafc464ccd64dd920c944e585..71044652d902cae7a2cb9213ac4e33b788e07e7a 100644
--- a/src/mapblock.cpp
+++ b/src/mapblock.cpp
@@ -1672,6 +1672,110 @@ scene::SMesh* makeMapBlockMesh(MeshMakeData *data)
 				collector.append(material_papyrus, vertices, 4, indices, 6);
 			}
 		}
+		else if(n.d == CONTENT_RAIL)
+		{
+			u8 l = decode_light(n.getLightBlend(data->m_daynight_ratio));
+			video::SColor c(255,l,l,l);
+
+			bool is_rail_x [] = { false, false };  /* x-1, x+1 */
+			bool is_rail_z [] = { false, false };  /* z-1, z+1 */
+
+			MapNode n_minus_x = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x-1,y,z));
+			MapNode n_plus_x = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x+1,y,z));
+			MapNode n_minus_z = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x,y,z-1));
+			MapNode n_plus_z = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x,y,z+1));
+
+			if(n_minus_x.d == CONTENT_RAIL)
+				is_rail_x[0] = true;
+			if(n_plus_x.d == CONTENT_RAIL)
+				is_rail_x[1] = true;
+			if(n_minus_z.d == CONTENT_RAIL)
+				is_rail_z[0] = true;
+			if(n_plus_z.d == CONTENT_RAIL)
+				is_rail_z[1] = true;
+
+			float d = (float)BS/16;
+			video::S3DVertex vertices[4] =
+			{
+				video::S3DVertex(-BS/2,-BS/2+d,-BS/2, 0,0,0, c,
+					0, 1),
+				video::S3DVertex(BS/2,-BS/2+d,-BS/2, 0,0,0, c,
+					1, 1),
+				video::S3DVertex(BS/2,-BS/2+d,BS/2, 0,0,0, c,
+					1, 0),
+				video::S3DVertex(-BS/2,-BS/2+d,BS/2, 0,0,0, c,
+					0, 0),
+			};
+
+			video::SMaterial material_rail;
+			material_rail.setFlag(video::EMF_LIGHTING, false);
+			material_rail.setFlag(video::EMF_BACK_FACE_CULLING, false);
+			material_rail.setFlag(video::EMF_BILINEAR_FILTER, false);
+			material_rail.setFlag(video::EMF_FOG_ENABLE, true);
+			material_rail.MaterialType
+					= video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
+
+			int adjacencies = is_rail_x[0] + is_rail_x[1] + is_rail_z[0] + is_rail_z[1];
+
+			// Assign textures
+			if(adjacencies < 2)
+				material_rail.setTexture(0, g_texturesource->getTextureRaw("rail.png"));
+			else if(adjacencies == 2)
+			{
+				if((is_rail_x[0] && is_rail_x[1]) || (is_rail_z[0] && is_rail_z[1]))
+					material_rail.setTexture(0, g_texturesource->getTextureRaw("rail.png"));
+				else
+					material_rail.setTexture(0, g_texturesource->getTextureRaw("rail_curved.png"));
+			}
+			else if(adjacencies == 3)
+				material_rail.setTexture(0, g_texturesource->getTextureRaw("rail_t_junction.png"));
+			else if(adjacencies == 4)
+				material_rail.setTexture(0, g_texturesource->getTextureRaw("rail_crossing.png"));
+
+			// Rotate textures
+			int angle = 0;
+
+			if(adjacencies == 1)
+			{
+				if(is_rail_x[0] || is_rail_x[1])
+					angle = 90;
+			}
+			else if(adjacencies == 2)
+			{
+				if(is_rail_x[0] && is_rail_x[1])
+					angle = 90;
+				else if(is_rail_x[0] && is_rail_z[0])
+					angle = 270;
+				else if(is_rail_x[0] && is_rail_z[1])
+					angle = 180;
+				else if(is_rail_x[1] && is_rail_z[1])
+					angle = 90;
+			}
+			else if(adjacencies == 3)
+			{
+				if(!is_rail_x[0])
+					angle=0;
+				if(!is_rail_x[1])
+					angle=180;
+				if(!is_rail_z[0])
+					angle=90;
+				if(!is_rail_z[1])
+					angle=270;
+			}
+
+			if(angle != 0) {
+				for(u16 i=0; i<4; i++)
+					vertices[i].Pos.rotateXZBy(angle);
+			}
+
+			for(s32 i=0; i<4; i++)
+			{
+				vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
+			}
+
+			u16 indices[] = {0,1,2,2,3,0};
+			collector.append(material_rail, vertices, 4, indices, 6);
+		}
 
 	}
 
diff --git a/src/mapnode.cpp b/src/mapnode.cpp
index 7e97a8d04e22ba2e85688a9bb37d3e018585da01..1c0a2740baec4ce198ca1b59493759ea1770e142 100644
--- a/src/mapnode.cpp
+++ b/src/mapnode.cpp
@@ -281,6 +281,17 @@ void init_mapnode()
 	f->solidness = 0; // drawn separately, makes no faces
 	f->air_equivalent = true; // grass grows underneath
 
+	i = CONTENT_RAIL;
+	f = &g_content_features[i];
+	f->setInventoryTexture("rail.png");
+	f->light_propagates = true;
+	f->param_type = CPT_LIGHT;
+	f->is_ground_content = true;
+	f->dug_item = std::string("MaterialItem ")+itos(i)+" 1";
+	f->solidness = 0; // drawn separately, makes no faces
+	f->air_equivalent = true; // grass grows underneath
+	f->walkable = false;
+
 	// Deprecated
 	i = CONTENT_COALSTONE;
 	f = &g_content_features[i];
diff --git a/src/mapnode.h b/src/mapnode.h
index 57335b74117ad159594e35f1d167cc99760cef0a..409fe2bcf97a5ddf0fb345a6346e7ddea9277c3f 100644
--- a/src/mapnode.h
+++ b/src/mapnode.h
@@ -107,6 +107,7 @@ void init_content_inventory_texture_paths();
 #define CONTENT_CLAY 25
 #define CONTENT_PAPYRUS 26
 #define CONTENT_BOOKSHELF 27
+#define CONTENT_RAIL 28
 
 /*
 	Content feature list
diff --git a/src/materials.cpp b/src/materials.cpp
index 7815f593ebecaa36d27ef3872f486efc9893c63e..a8a9a94a88771064c6b120fe091e1b9ef8292479 100644
--- a/src/materials.cpp
+++ b/src/materials.cpp
@@ -79,6 +79,7 @@ void initializeMaterialProperties()
 	setWoodLikeDiggingProperties(CONTENT_PAPYRUS, 0.25);
 	setWoodLikeDiggingProperties(CONTENT_GLASS, 0.15);
 	setWoodLikeDiggingProperties(CONTENT_FENCE, 0.75);
+	setDirtLikeDiggingProperties(CONTENT_RAIL, 0.75);
 	setWoodLikeDiggingProperties(CONTENT_WOOD, 0.75);
 	setWoodLikeDiggingProperties(CONTENT_BOOKSHELF, 0.75);
 	setWoodLikeDiggingProperties(CONTENT_CHEST, 1.0);
diff --git a/src/server.cpp b/src/server.cpp
index f40ed05a5e76fe4a62f27db300a68c83e10b0c48..4e9ff076fc3317692dbaefe404cf764ad4b37bd2 100644
--- a/src/server.cpp
+++ b/src/server.cpp
@@ -3915,6 +3915,26 @@ void Server::UpdateCrafting(u16 peer_id)
 				}
 			}
 
+			// Rail
+			if(!found)
+			{
+				ItemSpec specs[9];
+				specs[0] = ItemSpec(ITEM_CRAFT, "steel_ingot");
+				specs[1] = ItemSpec(ITEM_CRAFT, "Stick");
+				specs[2] = ItemSpec(ITEM_CRAFT, "steel_ingot");
+				specs[3] = ItemSpec(ITEM_CRAFT, "steel_ingot");
+				specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
+				specs[5] = ItemSpec(ITEM_CRAFT, "steel_ingot");
+				specs[6] = ItemSpec(ITEM_CRAFT, "steel_ingot");
+				specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
+				specs[8] = ItemSpec(ITEM_CRAFT, "steel_ingot");
+				if(checkItemCombination(items, specs))
+				{
+					rlist->addItem(new MaterialItem(CONTENT_RAIL, 15));
+					found = true;
+				}
+			}
+
 			// Chest
 			if(!found)
 			{
@@ -4163,6 +4183,7 @@ void setCreativeInventory(Player *player)
 		CONTENT_BOOKSHELF,
 		CONTENT_GLASS,
 		CONTENT_FENCE,
+		CONTENT_RAIL,
 		CONTENT_MESE,
 		CONTENT_WATERSOURCE,
 		CONTENT_CLOUD,