Computer History Museum Publishes Memories of the Programmer for NASA’s Moon Missions

This week Silicon Valley’s Computer History Museum posted a PDF transcript (and video excerpts) from an interview with 81-year-old Margaret Hamilton, the programmer/systems designer who in the 1960s became director of the Software Engineering Division at the MIT Instrumentation Laboratory which developed the on-board flight software for NASA’s Apollo program. Prior to that Hamilton had worked on software to detect an airplane’s radar signature, but thought, "You know, ‘I guess I should delay graduate school again because I’d like to work on this program that puts all these men on the Moon….’" "There was always one thing that stood out in my mind, being in the onboard flight software, was that it was ‘man rated,’ meaning if it didn’t work a person’s life was at stake if not over. That was always uppermost in my mind and probably many others as well." Interestingly, Hamilton had originally received two job offers from the Apollo Space Program, and had told them to flip a coin to settle it. ("The other job had to do with support systems. It was software, but it wasn’t the onboard flight software.") But what’s fascinating is the interview’s glimpses at some of the earliest days of the programming profession:
There was all these engineers, okay? Hardware engineers, aeronautical engineers and all this, a lot of them out of MIT… But the whole idea of software and programming…? Dick Batten, Dr. Batten, when they told him that they were going to be responsible for the software…he went home to his wife and said he was going to be in charge of software and he thought it was some soft clothing… Hamilton also remembers in college taking a summer job as a student actuary at Travelers Insurance in the mid-1950s, and "all of a sudden one day word was going around Travelers that there were these new things out there called computers that were going to take away all of their jobs… Pretty soon they wouldn’t have jobs. And so everybody was talking about it. They were scared they wouldn’t have a way to make a living. "But, of course, it ended up being more jobs were created with the computers than there were…." Hamilton’s story about Apollo 8 is amazing…


Read more of this story at Slashdot.

Slashdot

Officer cleared in the shooting of Ma’Khia Bryant.

https://gunfreezone.net/wp-content/uploads/2022/03/MaKhia-Bryant-shooting-officer-cleared-alt.jpg

I am sure you remember this case from last year:

The officer has been cleared of any wrongdoing (and rightfully so), but two things keep popping up that tick the hell out of me. First is the “Trayvonization” of Ma’Khia Bryant. By that I mean she is literally portrayed in image as a peaceful happy child rather than the violent woman armed with a knife, half a second away from inflicting a deadly injury to somebody unarmed and non-aggressive.  Just like to this day the image of Trayvon Martin is of that cute 12-year-old in a white hoodie rather than the late thug teen smoking and flipping the bird to the camera acting all gansta.

All the media outlets I checked have managed to keep the racial component out of their wording. No need for them to add anything since there are plenty of people in the comment section adding it. And then, there is the second part that ticks me off: Those Internet experts in the Use Deadly Force graduated from the University of TV Shows and PHD From the Jackie Chan Disarming Program.

I just would like to ask these idiots one simple question: How many stabs to the abdomen and chest would they be willing to take as long as the cops use “dialogue” and taser (with a 50% failure rate) on their attackers? One? Five? As long as it takes? Till the coroner picks up their body?

Inquiring minds want to know.

Gun Free Zone

One of the best-equipped and most experienced “preppers” I’ve ever encountered

https://blogger.googleusercontent.com/img/a/AVvXsEipknX1Ei8MMkWoR9BhGNENvvUlRqbx83zLfzavZWcrSfm5XgZ3-h3UKlHNpjXafiWDSnhaGxmYe9hZycZFhpDfH0xDjo6xu-39G1tX0nGD1AQ08WxpsXzp4o9cp-344SvZaGSiezXaDqPp6D_oGE1JRDuYqqSRCQ3yPnCvXpcIONk7p56Z0NlVfV0W=s72-c

 

We’ve spoken often about the need to make preparations for emergencies before they happen, and particularly in the current economy (which is enough of an emergency in itself, thank you very much!).  After I published another article about that recently, an online friend I’ve corresponded with over several years sent me a long and detailed e-mail about his emergency preparations.  Frankly, I’m very jealous of his stash (although "hoard" is probably a better term, in the sense of dragons hoarding gold, as depicted in fiction).  It’s certainly worth a great deal of gold in today’s economy.

In order to illustrate how well prepared you can be, if you work at it, I asked him for permission to reprint his e-mail here.  He agreed, as long as he wasn’t identified or his location given;  so, here’s "my older friend in the mountains".

Hi, Peter,

I saw your post this AM, thought I might chime in with a couple things. You probably already know most of this, but maybe some of it might be useful.

I’m extremely fortunate in that I began prepping a long, long time ago (when I moved to my present location about ten years ago one of my friends helping load the moving truck said – while loading a large stack of heavy boxes that had a "certain rattle" to them – "I bet they have stores in your new location that sell canned food, too."). I currently have about 1500 cans of food on hand (FYI, a 12"L X 9"W X9"H box holds 24 cans, each 16 ounces, with space between them for plasticware – knives, spoons, forks – with P38s and P51 openers taped under the box lid, 8 cans of protein, 8 of vegetable and 8 fruit, makes a "food unit"; I don’t keep canned fruit over 6 months past it’s "best buy" date because meat and veggies are cooked before canning but canned fruit in my experience will start leaking soon beyond six months after that best buy date) plus 250 cases of freeze dried food, about evenly spit between Mountain House and Augason farms; there’s 300 gallons of drinking water in 5 gallon jugs, 5 Big Berkey filters with 2 sets of spare filters for each, a pair of 250 gallon water tanks to capture "flushing and irrigation water" from the roof gutters, 1,000 large coffee filters to screen "stuff" from water, over a dozen Sawyer and LifeStraw personal water filters, 4 methods of making coffee from a Mr. Coffee to coffee presses to two old fashioned percolators and a simple "funnel & filter drip" method.

I won’t mention ammunition, firearms or magazines because, well, gosh, who would need any of that? The tires on the truck are 12 years old but still in good shape and just past half-worn (it’s garage kept so no sunlight damage), I have 4 new tires (3 years old) mounted on spare steel wheels (one pair gets pumped up from 4 PSI to 30 PSI and has snow chains installed every November to April, just in case we get ice. It’s faster and easier to swap wheels than install chains in the snow, especially with a cordless ½ inch impact driver) and there’s a stack of 6 brand new tires bought recently on sale under a tarp in the corner of the garage waiting to be mounted. I have 10 NATO cans with non-ethanol gas that gets rotated – every few months I’ll refill the truck tank with 10 stored gallons and refill the cans, using Pri-G fuel stabilizer so the stored gas is never over 12-15 months old (Pri-G is great stuff, BTW, Commander Zero recommended it so I tried it, and my "test case" was non-ethanol gas stored for 3 years in 2 NATO cans with Pri-G and it was fine). I have 4 empty NATO cans on the shelf and 12 more in the garage attic (4 were "surplus, stored" on sale from Coleman Outdoors – covered in dust and dirt from storage but never used so the inside is pristine; I got them cheap so those are my "giveaway cans" if someone is in dire need). I was lucky to buy all those cans 12-18 years ago when they were much less expensive. I know they’re very expensive now, but the real NATO cans, now made in Latvia, are the only way to go; the cheaper knockoffs just aren’t the same. I will refrain from commenting about the CARB-approved spouts that are mandated now except to say that once government gets involved nothing improves.

FYI, if you get NATO cans get several extra NATO spouts – nothing else will fit the cans, and the regular spouts will need to be modified – this if for the old style, pre-CARB spouts, haven’t tried it yet on a CARB spout – I removed the rubber flexible section and installed 16 inch length of 1” ID clear plastic tubing from the home center plumbing dept. and a 1” male-threaded-to-3/4” ribbed nipple fitting; now the end fitting will reach ANY gas filler, fit unleaded fillers and 20 liters of gas empties in 50 seconds. Pro Tip: use safety wire wraps or screw clamps to secure all the parts together so nothing comes apart -you absolutely do not want the hose coming off the nozzle or getting loose and leaking when you’re pouring gas.

In the truck I’ve got two 30 ft Nylon tow ropes, tree straps and a couple 20 ft "trucker chains," a bunch of 5 ton steel shackles and "soft shackles," (soft shackles are much more expensive but a whole lot safer) two 4000 lb and one 6,000 lb comealongs in the garage, two high-lift off road jacks, a 2 ton and a 3 ton scissors jack, a 3 ton, 5 ton and a 20 ton hydraulic jack, double thickness 3/4 plywood "jack plates" for soft soil (I used to live where you either put something large, solid and flat under a jack or you just drove the jack into the ground – Pro Tip here – two pieces of 3/4 plywood glued together with polyethelyne glue – Gorilla Glue – cut to the largest size that will fit under the passenger seat (mine are 17” wide X 19” long), with 3/8" holes near each corner for anchoring with 12" landscape spikes and holes for bolting the high lift jack to the plate will save the day. Make THREE double 3/4" plates, two will probably fit under the seat and worst case, put two together right against each other with the third across the top for the jack. I got a friend’s high-centered 4X4 GMC out of mud once with that trick).

Plano Plastics makes heavy duty 56 quart “cargo boxes” (Walmart, Amazon, etc.) that will hold a lot of stuff, not waterproof but will stay dry in the rain, put them in the bed and strap ’em down. I’ve got a couple Marine versions (gasketed lids), one is the “Road Box” with food, water, cooking equipment, blankets, rain gear, etc. the other is the “Unstuck Box” with an extra tow cable, hydraulic jack, extra soft shackles, a tree saver strap, etc. If I’m going farther than I think I can walk back from, those go in the truck. There is already a very good assortment of general mechanical tools under the back seat, and I never, ever, venture outdoors without a large diameter projectile expeller on one hip and a Tube of Dark Repellent on the other (I really like Elzetta lights, but Surefire and Streamlight are good, too, it’s just that Elzetta allows you to custom build a tactical light the way you want it.).

Since COVID hit and drove freeze dried food prices through the roof I’ve stopped buying FD food, shifted to more readily available canned and dry food and spent the last 2 years wracking my brain about the multitude of less obvious stuff – nails, screws, light bulbs, extension cords, lumber, rope, clothing, shoes and boots, home repairs and upgrades, spare roof shingles, 2X4s, batteries (I’ve bought only lithium batteries (AA and AAA) for the past 2 years), spare parts, etc. etc. and have added to those supplies as availability and budget allowed. For almost 3 years I’ve basically spent every dollar I had that wasn’t committed to something on "prepping supplies."

A year after I bought the house I’m in now I had it converted to "dual fuel" – it had an electric heat pump so I paid the local utility $350 for the gas line hookup and replaced the heat pump air handler and backup electric heat with a natural gas furnace (I can control the switchover at the thermostat – air-to-air heat pumps lose a LOT of their efficiency below about 38F so I’ve got it set to stop using the heat pump and switch to using NG below 40F), plus a natural gas tankless water heater – the NG furnace draws 865 watts on 120 volts to operate, the NG tankless water heater draws 92 watts and the UPS it’s on (an APC 1500) protects it from surges AND will power it for 3 weeks of 2 4-minute showers/day (both the furnace and water heater can be converted to propane if it comes to that) so I can power the NG furnace (and the NG water heater) with a Honda EU2000 generator. I have a propane fireplace and as soon as I can find the one I want for a price I can afford, I’ll install a standalone propane heater to sit in front of the fireplace and connect it to where the propane logs connect because the heater will be much more efficient; there’s 200 gallons of propane in two 100 gallon tanks (2 is one, 1 is none….) outside and I get them topped off twice a year. If I can, and I doubt it will be possible now, I’ll replace my electric stove with a propane stove so I’m self sufficient for cooking (FYI, natural gas is about 1050 BTU per cubic foot, propane is just over 2500 BTU). I do have 3 Coleman camp stoves, 2 kerosene cook stoves, 2 propane burners (previously used turkey fryers I got from a thrift store), some firebrick to make a rocket stove and 4 propane/butane single burner units, and 3 “camp-out style" cooking grates that will work over any fire.

I’ve accumulated quite a few pieces of cast iron cookware, some inherited, some purchased, and 2 sets of "budget cheap-o stainless cookware" that I won’t mind using over a wood fire (or gift one to someone in dire need). I’ve got 4 propane heaters (all of them Mr Buddy heaters, the largest rated at 80,000 BTU) and five 40 lb propane tanks plus the 20 lb on the grill and a spare 20 lb. Seven Aladdin lamps, one of which is wall mounted, 2 kerosene heaters (one 23k BTU vertical convection, one 50K BTU fan forced,which also requires 120 volts to work) and 25 gallons of kerosene in NATO cans. Six railroad-style kerosene lanterns, and a pair of Bryt-Lyte German pressure-fed liquid fuel lanterns that will burn ANYTHING that’s liquid and flammable. I have 44 Stanley parts organizers in a cabinet in the garage filled with small parts and supplies, another 12 in the “South Warehouse” filled with gun parts. I’m a gear head who worked his way through college in the motorcycle business and have rebuilt V-8s and manual transmissions and more motorcycle engines than I care to remember so I already had a good supply of mechanics’ tools and quite a few carpentry tools. My family owned a 200 acre farm that from my late teens into my thirties I was the "maintenance manager" for, so I inherited, bought and accumulated quite a few "manual labor" tools because extension cords wouldn’t reach 2000 feet to the back fence. Learned a lot doing that, turning sweat into knowledge.

Pro Tip here – I installed two whole-house surge protectors (and yes, I used a very experienced licensed electrician even though it cost money), one a Siemens unit in the outside meter box where the power lines come in and a Square D in the distribution panel indoors (indoors, I can shut off the main power outside, and have, to swap breakers or install the surge protector, but, sorry, I’m not going near the high amperage lines feeding into the meter because there’s no way to make them “cold.” I do know my comfort zone and my limits.) There is a large possibility that electrical power, however it’s produced, will be unreliable in the future and power drops and surges could easily occur. It would be unpleasant in the extreme to have a surge kill your refrigerator, especially when it might take weeks or months in a supply shortage to get a new one; that’s personal experience talking, make of it what you will.

I gave up my guest room 3 years ago, it’s now the "North Warehouse" and the spare bedroom next to it is the "South Warehouse" – both are full of wire rack shelving with barely room to move in either room. Part of my plan has been to have enough supplies to support not just myself but a few others who may be in dire need AND WHO HAVE PROVED THEMSELVES BOTH WORTHY AND TRUSTWORTHY. Whatever I give out won’t come free, it will have to be earned by the recipient demonstrating he or she is worthy of it; until then I’m Sgt Schultz on Hogan’s Heroes about prepping – "I know nothing."

I’m on a good sized lot in a semi-rural location but don’t have enough space to grow much of anything, and what space I do have is too heavily shaded in summer, but a friend lives a moderate distance away on rural acreage and I’ve lent some of my brawn & brain to helping him so that is a potential source of future food, and a potential "bug out location." That will mean sleeping in the barn, probably, but I can make a pretty good bed out of freeze dried food cases and I have sleeping bags, wool blankets and a couple military sleep systems (I also have a cheap Chinese tent stove, so worst case I can produce some heat…). I’ve also got a couple 2-person tents, just in case, and it’s vitally important that should it develop that bug-out be required I will not show up empty handed, I’ll arrive with as much food, supplies and equipment that the truck can carry.

I’ve kept $5K cash in an emergency fund while spending almost everything else, but given what happened in Canada I’ve started cleaning out the bank account every month with whatever is left after bills are paid and keeping it on hand in cash. I’ve also opened a second set of accounts at a local credit union because while it probably won’t fare any better than the mega bank where my main accounts are in the event of punitive government action it does provide additional options. I absolutely, positively do not trust our government to not follow Canada’s example. I have not bought any "junk" silver since the price went above $20/ounce and was lucky enough to be paying *very close* attention when it was in the $12-$16 range, which is all I’ll say about that. I see no need for having a lot in the way of gold because the smallest denomination is too large for everyday use, but having some can be handy (I consider firearms, ammunition, especially canned food, spare parts and supplies, and very especially knowledge, skills and willingness to work, much better for bartering purposes, but bartering is high risk so I look more at that as “supplying assistance to those whom I trust who are in dire need.”

As a young kid – age 5 through 7 – my cousin and I (we’re the same age and both of us were "city kids") spent much of our summer on Grandma’s farm (part of which I inherited years later). Once a month we’d head to the "big city" 35 miles way ("big city" meant a population of 10-12,000) for a shopping trip, the rest of the time we made or grew it ourselves, improvised or did without. I didn’t appreciate it at the time but that was an education that has returned full value for my entire life.

There’s no question in my mind that I have been blessed beyond belief and human understanding. I have no idea why or to what purpose, I assume I’ll find out when I need to know.

Peter, you mentioned buying a generator to power the house; some years ago I went through several hurricanes and a tropical storm in a couple months. With the first one we lost power for 6 days, the other two killed power for about a day or so each. Fun times, let me tell you. I had a Honda ES6500 generator and a Honda EU3000i generator. When I lived in the mountains we had well and septic and being in the mountains we lost power from snowstorms, ice storms and strong thunderstorms and I needed 240 volts with 30 amps to run the well pump. I got the ES6500 because it was 120 / 240 volts. 52 amps, and electric start, which made it possible for others to start.

Honda built that generator with a 359cc 2-cylinder water cooled car engine (cars in Japan are taxed based on engine size, the one that uses the 359cc 2-cylinder is a tiny 2-person "car" no larger than a US golf cart). AFAIK, Honda no longer offers that generator, replacing it with a 7000 watt super-quiet air cooled generator. I’m trying to find budget dollars to replace the 6500 with the newer super quiet 7000. The ES6500 can run for DAYS as long as it’s kept fueled – it’s a car engine, after all, water cooled, large oil capacity, spin-on oil filter, etc. but I doubt I need days of run time now.

I got through the hurricanes with the Honda EU3000i; the fuel tank is 3.4 gallons and I powered the refrigerator and some fans (daytime) with it and the fridge and a window air conditioner at night, about 14-16 hours total run time on about 2.5 gallons. The 3000 is 120 volts only so it won’t power 240 volt equipment. Other than well pumps, that’s not too big a deal – electric water heaters can be "adjusted," central AC and heat pumps need 20-30,000 watts, so a MUCH bigger generator is required. Honda calls for oil changes on the 3000 at – IIRC – 40 hours. I use nothing but high grade synthetic Amsoil in it and was religious about changing it at 60 hours.

What I did – my older house in the mountains came with the usual ”builder grade” 6 gallon draw down well pressure tank so the pump started, stopped, started, stopped, etc. I replaced the small tank with 2 larger tanks, each with 46 gallon draw down. I plumbed in for 3 tanks, could only find 2 readily available of that size. The result was the pump would run for about 13-15 minutes to refill both tanks then shut down. I replaced shower heads with very low flow heads (1.5 GPM) and taught the family about "navy showers" when we lost power. We had the old 4.5 gallon per flush toilets, I replaced one with a new 1.6 gallon (it was lousy because it was very early in the government-forced toilet conversion, they’re much, much better now) and taught "yellow, let it mellow, brown, flush it down" so we could get through 12-15 hours without having to fire the generator up to refill the draw down tanks. 1-3 long pump runs daily was easier on the pump and the generator. I won’t go into detail about how I connected the generator to the house, but I did have a 50 amp circuit with receptacle in the basement for a welder. If going this route you really, really, really need a transfer switch, even a manual one, installed by a qualified and licensed electrician. It also pays to have heavy duty cords – I made two 125 ft cords, one with 10-3 SO cable (30 amp) and one with 8-3 SO (42 amp) to handle both 120 volt power legs and neutral and one 100 ft with 8-4 (both power legs, a neutral and a ground, for a 4-slot twist lock welder receptacle, uh, well, just in case I need to power the welder…..)

People tend to buy larger generators than they need. If you have to power a well pump you’ll need 240 volts at 30 amps or better, but for normal household stuff, 120 volts with 2500-4000 watts will do it. The larger the generator the more fuel it requires and fuel will be the #1 issue in SHTF situations.

I was able to provide some hot water – I had an 80 gallon electric water heater – 240 volts, 30 amp circuit, two 5500 watt heating elements. I replaced one of the 5500 watt heating elements with one of a lower wattage and modified the system from the breaker panel to the water heater to allow powering only one heating element, trying to power both would exceed the total wattage available from even a 6500 watt generator (the thermostats in electric water heaters “flips” power between the upper and lower heating elements, powering only one at a time; running even one 5500 watt element would be just under the rated 6000 watt maximum steady draw for the generator, and while both elements SHOULDN’T be powered by the thermostats at the same time, doing so – even for a very brief period – would quite severely overload the generator and I did not want to risk damaging it). Doing this involves advanced electrical knowledge and is very much a non-standard installation, so hire a highly qualified licensed electrician who has experience in this area. FYI, it is possible to run an electric water heater element on 120 volts but efficiency falls off a lot (don’t ask how I know). Again, hire a very qualified licensed electrician for any of this; working with electricity is hazardous and long time licensed electricians should know how to do it.

I could run the generator for about 15 minutes to fill the draw down tanks, then switch the pump breaker off and the water heater on to heat water for about an hour, then shut the generator off. Heating 80 gallons with only one lower wattage element takes a while, and I’d suggest reducing the water temperature by adjusting the thermostat control for the one element that’s receiving power; 120-125 degrees is nice and toasty for showers and laundry, but you can get by with 100-105, and yesterday would be a good time to add extra insulation to your water heater. The best solution would be a propane-fired tankless water heater and had I stayed in that house I would have spent the money and gotten one installed. It was high on the list for this house soon after I moved in, but natural gas was cheaper and readily available.

Pro Tip – Don’t do a “dedicated laundry day.” It’s handy, and maybe more efficient, to do all the laundry in “batch mode” on one day per week, but if SHTF – or your area is subject to random power outages….. your capacity to run the washer, and/or heat water, will be very limited. If the poop strikes the impeller having a large pile of dirty laundry to process will be an “issue.” Instead of batch mode do “continuous flow” – if you have enough to run a load, even a small one, do it. Worst case, you’ll need something other than the dryer to dry it. And have enough clothesline rope on hand to dry it, do it. Fence posts are set on – usually – 8 ft centers, I cut brackets out of 5/4 (standard decking boards), bolted them to fence posts, drilled and strung clothesline through the brackets. Presto – 96 feet of drying clothesline along the back fence. I don’t use it all that often, and it won’t work on rainy days or when it’s below freezing, but the rest of the time it works fine.

For 120 volt needs I used the 3000 because it was MUCH quieter and more economical on fuel – fridge, whole house fan (summer), window AC at night, oscillating fans (summer for comfort and winter to spread the heat from the wood stove), the 120 volt stuff on extension cords. I have lots of 12 and 10 gauge 50ft and 100 ft cords, Tripp Lite makes 15 and 20 amp multi-outlet strips with 15 ft and 20 ft cords. During the hurricane power outages the 3000 did the same work.

Pro Tips: test your fridge – get a wireless fridge & freezer thermometer (Amazon, about $35) set freezer to max cool, the fridge to "max cool without freezing stuff" and let it stabilize for 24 hours, then unplug it and DO NOT OPEN THE DOORS. Watch it for temp rise, when fridge temp gets to about 46-48F plug it in and see how long it takes to get back down to 33F. It will continue to rise beyond 48F for 30-60 minutes before temp starts dropping. The freezer probably will not go above 10F. That time – 33F to 48F is how long it can go without being powered up, the return trip down to 33F is how long it needs power to cool down. My fridge would handle 7 hrs off / 2.5 on so I did 6 off / 3 on during the day; the 3000 ran most of the night to power the fridge and a window AC because having a cool room to get 6-7 hours restful sleep makes it possible to get through the day without accumulating exhaustion (if you were ever a field troop in the military you know all about accumulating exhaustion and how it impacts physical and especially mental performance).

That DO NOT OPEN THE DOORS thing will require training family members. Especially teenagers. The fridge doors get opened only when the fridge has cooled down and the generator is continuing to keep it cool. That will not be an easy or simple training procedure but it can be done. A cooler for drinks, sandwich stuff, etc. will turn out to be handy. So would handcuffs and leg irons, but…

I thought about buying insulation panels – iso foam is R 6.5 / inch, so adding a 2" layer of insulation is R 13 – to the exterior sides of the fridge (and now, my chest freezer) to buy more "off time" between generator runs but have not done it. Yet. I need to research that and see if it’s actually worthwhile. It should be, but I want to see some real data on it. There are ultra efficient refrigerators and freezer – Sun Danzer is one brand I’m aware of but there are several – that usually run on 12 volts to make it easy to power them with solar panels, and have several more inches of insulation than regular fridges and freezers. They’re not cheap, almost always in chest style and nowhere near the size of a regular home refrigerator, but if I had the space I’d get two, one freezer and one refrigerator. Propane fridges work but I don’t know enough about them except that they seem to consume a lot of propane; I need to do more research.

I used brand name all-copper extension cords and the Tripp Lite power strips during the hurricanes to distribute 120 volts from the 3000; the Tripp Lite strips aren’t cheap, $30-45 each back then but worth it. The cheap stuff from home centers and online just don’t cut it, they’re frequently copper-clad aluminum (especially auto jumper cables). Avoid those, go with pure copper. More expensive but worth the extra $$.

During the hurricanes I had a 30 inch industrial fan I had bought from a dry cleaner going out of business (replaced the bearings, belt and motor – originally it moved 9000 CFM and drew 480 watts, when I was done it moved 4200 CFM on 240 watts). I grew up in a large city in the early 1950s in an older apartment building; no one had AC then, everyone had a 20" 3 speed paddle-bladed window fan in the kitchen window that never got shut off from April to October (those fans were everywhere back then but are “antiques” or “vintage” now and sell for $200 or more….). By opening the right windows and doors air flow through the apartment could be "regulated", especially to flow over the beds at night. I did the same thing with the 30" industrial fan and used several 16-20" oscillating fans with 25 ft 14 gauge extension cords to keep air circulating in the dead spots. It was warm, humid air but it was moving warm, humid air.

The EU3000i is ultra quiet – sitting in the back yard no one knew I had a generator running. That meant it didn’t attract generator thieves, or keep the neighbors awake. The ES6500, well, it is very reliable, but stealthy it ain’t.

I loaned the 6500 to a neighbor, when they had it running it sat in the driveway of their house powering their fridge and a couple fans and with extension cords running to the refrigerators and a couple oscillating fans in the houses to either side. They had to rush out and buy gas cans, gas, fans and extension cords. In the mountains we had a 120 volt whole house fan that…uh…."could be powered." I won’t say how. FYI, DO NOT run a generator indoors, not even in a garage with the door open or on a carport. Gasoline engines produce carbon monoxide (CO) and every year during power outages people die from CO poisoning caused by generators – CO attaches itself to hemoglobin in blood easier than oxygen does and prevents oxygen from attaching to hemoglobin, the result is no oxygen gets distributed through the bloodstream and death comes quickly. The body will tell you when there’s too much Carbon Dioxide (CO2) in the bloodstream or lungs – hold your breath for a while, you’ll see – but that reaction isn’t present for Carbon Monoxide. Just DO NOT RUN ANY CARBON MONOXIDE PRODUCING EQUIPMENT ANYWHERE INDOORS OR NEAR INDOORS.

My current house doesn’t have a whole house fan so I bought a 20 inch Air King "whole house window fan" from Amazon – 3 speeds, 1700 – 3400 CFM, it brings me back to my childhood days in the big city apartment opening the right windows and doors, but it works, and I have several 16" oscillating fans and a 22" and a 24" oscillating pedestal fans from Northern Tool. It takes a very large generator to run central air conditioning, so figure on fans, they can be run with a low wattage generator. Look for sales on fans in late summer/early fall and stock up.

I now also have two Honda EU2000i generators (bought one some years ago, the other came as a gift). They’re ultra quiet and like the 3000s, can be coupled together with either Honda’s wiring kit, or a home built wiring kit, to double the wattage; again, this requires advanced electrical knowledge so consult with a licensed and very experienced electrician or just buy the Honda kit. The new ones are now rated at 2200 watts. Still always 120 volt, but going to 240 volts is expensive; unless you really need 240 volts for something I’d stick with 120V in a high quality quiet generator (I like the Hondas because I have experience with them, but there are several other comparable manufacturers). The Cheap-O $500 generators from home centers and big box stores etc. often will output 240V but they’re built with – usually – inexpensive engines, although some use the Honda air cooled engines which are very good, but they’re all noisy, even the Honda engines, and use more fuel. They’re also usually not designed for prolonged continuous use.

The Honda 2000s (now they’re 2200 watts) have a small fuel tank – 1.25 gallons – so as economical as they are they’ll still need refueling more often, the RV crowd has a way of getting around that with an auxiliary tank arrangement (just search "RVs and Honda 2000 generator extra tank"). A pair of new 2200s will be less than one new EU3000, they can be coupled together, most of the load you’ll be applying will be 120 volts and less than 1600 watts, for loads above that use both generators and the coupling kit, but most of the time one EU2200 will do the job. And, you have "2 is one and 1 is none." My EU3000 is 158 pounds (some of which is because it also has an electric starter, which I needed back then, and the new ones are lighter), my EU2000s are just under 50 pounds each, and one can be powering stuff while you’re changing oil in the other.

Get lots of extra oil, Honda calls for oil changes every 24 hours of use on the EU2000s. I ran my EU3000 for 60 hours between changes, but I use nothing but very high grade synthetic oil (Amsoil) in everything and was religious about not exceeding that time limit. Pro Tip – Get lots of extra oil anyway. It’s going to be more expensive and harder to get so get plenty now, you’ll need it. Don’t forget filters too (oil AND air), and spare drain plug gaskets is a good idea, too.

During the hurricane power outages I used my propane grill for cooking and making coffee (although the EU3000 would power the 1800 watt electric coffee maker, it wasn’t happy about it….), I still have the same grill, plus the other heat sources I described earlier. Multiple heat sources is good, develop them if you haven’t already.

During the hurricane power outages we never lost county water and it was late summer, so cold water from the spigot was 82-84F. I did have a Zodi water heater (look it up on the internet) which I used a few times, but most of the time 82F from the shower head was tolerable. Here we have winter (I’m back in the mountains again) and the water temp gets as low as 48F, so I’m keeping the Zodi handy. FYI, just like gasoline-burning generators, propane-burning water heaters like the Zodi produce carbon monoxide (CO) so DO NOT use them indoors. One trick is a 5 gallon bucket – on camping trips I’ve put both the pickup and spray head in a bucket and let the battery powered pump recirculate the water heated by the Zodi until it’s warm enough, then shut the propane burner off and remove the 1 lb propane cylinder and take the Zodi and the bucket of hot water indoors to shower with. It’s more work, but I know how dangerous CO is and I refuse to take risks with it.

One thing I strongly recommend, and with your military and prison work background you’re probably aware of it, is to perform a SWOT Analysis – Strengths, Weaknesses, Opportunities and Threats – to examine in detail where you are, where you need to be and what’s the delta between those points. A good SWOT Analysis is not a simple, or short, task but a very worthwhile one.

There’s lots more, but this is too long already. If I think of more that’s important enough to bother you with I’ll send another email. And, I apologize for the typos. I was typing fast and I didn’t run spell check on it.

(signed)

Your Older Friend in the Mountains

I’m green with envy at his extensive supplies and preparations.  I hope and pray he never needs them;  but, if push should come to shove, I bet he’ll be a lot better off than almost anyone in his area (not to mention his entire state!).  If Miss D. and I find ourselves trapped far from home, but near him, when an emergency arises, I daresay he might receive an emergency e-mail asking for sanctuary.

So . . . how do your preparations compare to his, friends?  Mine can’t hold a candle to them.  (Note to self:  check candle supplies, add to shopping list . . . )

Peter

Bayou Renaissance Man

Playing a Water-filled Piano

https://theawesomer.com/photos/2022/03/filling_a_piano_with_water_t.jpg

Playing a Water-filled Piano

Link

“I know this looks bad… but I’m doing it for the sake of science.” Mattias Krantz likes to do some crazy and borderline sacrilegious stuff to pianos. In this clip, he took an old piano, waterproofed it, then filled it with bucket after bucket of water to see what it would sound like when its strings and soundboard were submerged.

The Awesomer

Comic for March 09, 2022

https://assets.amuniversal.com/52969ab078a3013a9a6f005056a9545d

Thank you for voting.

Hmm. Something went wrong. We will take a look as soon as we can.

Dilbert Daily Strip

Infinity Bicycle

https://www.toxel.com/wp-content/uploads/2022/03/infinitybicycle01.jpg

Extremely cool bicycle concept with futuristic all-wheel drive and hubless wheel design inspired by the infinity symbol. “The Infinity Bicycle” designed by Stephan Henrich features innovative monotire chain design that forms a temporary rim in the wheel area and a dented belt-drive in the center area. Monotire is propelled by a crank over a short […]Toxel.com

A Practical Guide to Containerizing Laravel Applications With Docker

https://static.adevait.com/2022/02/A-Practical-Guide-to-Containerizing-Laravel-Applications-With-Docker.jpg

Due to its flexibility and ease of usage, Docker has become one of the most widely used, if not the most widely used, methods of distributing software.

In most cases, putting an application inside an OCI image and distributing it through one of the popular image registries is pretty easy. But not in the case of a Laravel application. When you’re packaging a Laravel or PHP application in general, it’s not just the application.

Depending on what your application is doing, it’ll depend on PHP-FPM, composer, a bunch of packages and NGINX, at the very least. Although spinning up separate containers for php-fpm and NGINX is not that much of a hassle in development, in a production environment, the best way is to combine PHP-FPM and NGINX in a single image and develop as close to production as possible.

In this article, I’ll walk you through the process of containerizing a Laravel application for both the development and the production environment.

For your convenience, I’ve uploaded my code to this repository, feel free to fork and modify it.

One more disclaimer before I begin: Things like containerizing or deploying an application can be done in several ways. This is just one of them that I’ve used in past. If you know a more optimized way of doing this, please share in the comments and take everything I say with a grain of salt.

Creating a Base Image

When it comes to containerizing a PHP or Laravel application, it’s common practice to use the official php image as a base. Nothing’s wrong with that, but that image doesn’t come with NGINX pre-installed. Even if you manage to install NGINX on a PHP image, it can be tricky to keep both NGINX and PHP-FPM running.

There are a number of pre-built base images out there that come with both PHP-FPM and NGINX. One such image that I often use, is the webdevops/php-nginx image. This is an excellent image with tons of customization options and easy-to-follow documentation.

In this article, I’ll not use any pre-built base Laravel docker image. Instead, I’ll show you how I make my own base image. The method I follow is not original. Bret Fisher has created the php-docker-good-defaults repository quite some time ago. I’ve made a fork of the repository, ported it from Debian to Alpine, and made necessary modifications as per my needs.

To follow along, you’ll need a Laravel application. Feel free to clone mine from this repository. Once you’ve cloned the code base, open it using Visual Studio Code or whatever code editor you like and take a good look at the directory structure.

.
├── Dockerfile
├── Makefile
├── docker
├── docker-compose.yaml
├── php-nginx-base.Dockerfile
└── src

This is how the project directory should look. The src directory here contains the actual Laravel application code. The docker directory contains mostly configuration files needed by Docker. The php-nginx-base. Dockerfile is the code for the base image (you’ll learn about it in this section). The Dockerfile contains the code for the application image, derived from the aforementioned base image. The docker-compose.yaml file is for using Compose in development and finally, the Makefile contains easy-to-execute one-liners for substituting commonly used long commands. This structure is nothing mandatory. You can come up with your own structure. Feel free to experiment.

Open up the php-nginx-base.Dockerfile file. The code for this file is as follows:

FROM php:8.1.3-fpm-alpine3.15

ENV NGINX_VERSION 1.20.2
ENV NJS_VERSION   0.7.0
ENV PKG_RELEASE   1

# install necessary alpine packages
RUN apk update && apk add --no-cache \
    zip \
    unzip \
    dos2unix \
    supervisor \
    libpng-dev \
    libzip-dev \
    freetype-dev \
    $PHPIZE_DEPS \
    libjpeg-turbo-dev
    
# compile native PHP packages
RUN docker-php-ext-install \
    gd \
    pcntl \
    bcmath \
    mysqli \
    pdo_mysql
    
# configure packages
RUN docker-php-ext-configure gd --with-freetype --with-jpeg

# install additional packages from PECL
RUN pecl install zip && docker-php-ext-enable zip \
    && pecl install igbinary && docker-php-ext-enable igbinary \
    && yes | pecl install redis && docker-php-ext-enable redis
    
# install nginx
RUN set -x \
    && nginxPackages=" \
        nginx=${NGINX_VERSION}-r${PKG_RELEASE} \
        nginx-module-xslt=${NGINX_VERSION}-r${PKG_RELEASE} \
        nginx-module-geoip=${NGINX_VERSION}-r${PKG_RELEASE} \
        nginx-module-image-filter=${NGINX_VERSION}-r${PKG_RELEASE} \
        nginx-module-njs=${NGINX_VERSION}.${NJS_VERSION}-r${PKG_RELEASE} \
    " \
    set -x \
    && KEY_SHA512="e7fa8303923d9b95db37a77ad46c68fd4755ff935d0a534d26eba83de193c76166c68bfe7f65471bf8881004ef4aa6df3e34689c305662750c0172fca5d8552a *stdin" \
    && apk add --no-cache --virtual .cert-deps \
        openssl \
    && wget -O /tmp/nginx_signing.rsa.pub https://nginx.org/keys/nginx_signing.rsa.pub \
    && if [ "$(openssl rsa -pubin -in /tmp/nginx_signing.rsa.pub -text -noout | openssl sha512 -r)" = "$KEY_SHA512" ]; then \
        echo "key verification succeeded!"; \
        mv /tmp/nginx_signing.rsa.pub /etc/apk/keys/; \
    else \
        echo "key verification failed!"; \
        exit 1; \
    fi \
    && apk del .cert-deps \
    && apk add -X "https://nginx.org/packages/alpine/v$(egrep -o '^[0-9]+\.[0-9]+' /etc/alpine-release)/main" --no-cache $nginxPackages
    
RUN ln -sf /dev/stdout /var/log/nginx/access.log \
    && ln -sf /dev/stderr /var/log/nginx/error.log
    
# copy supervisor configuration
COPY ./docker/supervisord.conf /etc/supervisord.conf

EXPOSE 80

# run supervisor
CMD ["/usr/bin/supervisord", "-n", "-c", "/etc/supervisord.conf"]

At first glance, this may seem very complicated, but it isn’t. The base image uses the official php image as its base. To be precise, the 8.1.3-fpm-alpine3.15 version of the official image. The first line of the code, FROM php:8.1.3-fpm-alpine3.15 sets the desired image as the base. When choosing the base, make sure to use a fixed Alpine version such as alpine3.15 and not the edge version. Otherwise, the NGINX installation step may fail. To figure out which Alpine versions are supported, check out NGINX’s package index page. At the time of writing, Alpine 3.15 is the latest supported version, hence I’m using the php:8.1.3-fpm-alpine3.15 image.

Then there are three ENV instructions, setting three environment variables, NGINX_VERSION, NJS_VERSION, and PKG_RELEASE, needed for installing NGINX as you’ll see very soon.

After the environment variables, there is a single RUN instruction for installing some necessary packages into the base image. If you’ve worked with Ubuntu or Debian in the past, apk for Alpine Linux is what apt is for Ubuntu or Debian. It’s a package manager. The apk update command updates the package list. The apk add command is for installing packages and the --no-cache option instructs apk to not cache the downloaded packages. This will keep the image smaller in size.

The zip and unzip packages are self-explanatory. dos2unix is needed for converting CRLF line endings to LF. supervisor is a process control system that is used for keeping both NGINX and PHP-FPM running at the same time. libpng-dev, freetype-dev, and libjpeg-turbo-dev is needed for GD, a widely used graphics library. libzip-dev is needed for igbinary serializer support and $PHPIZE_DEPS is a collection of multiple packages needed for installing packages from PECL.

The second RUN instruction uses the docker-php-ext-install script to install some core PHP extensions. This is script included in all variants of the official PHP image and allows users to install any core PHP extensions. The bcmath package is required by Laravel. The other ones are required by most of my projects. If you use a different RDMS such as PostgreSQL or Oracle, feel free to swap mysqli and pdo_mysql packages with whatever you need.

The next RUN instruction uses the docker-php-ext-configure package to configure packages installed using the docker-php-ext-install script. The aforementioned GD package needs to be configured properly otherwise it may result in unexpected bugs. The docker-php-ext-configure package is part of the official PHP image.

Not all packages are available through the PHP core. Such packages will have to be installed from PECL. The next RUN statement installs some additional packages using pecl. The zip package is needed for working with archives. igbinary is required by Redis if you want to enable the igbinary serializer support. Finally, I use Redis in more or less all my projects for caching, hence the redis package is being installed. Any package installed from PECL has to be enabled manually using the docker-php-ext-enable script. This script also comes built into the official PHP images.

Packages installed in this article are the ones I usually need in more or less all my projects. Feel free to cut some of them off or add any additional packages that you may need. It’s your base image, so make it your way.

After the PHP packages are installed, the next step is to install NGINX. As you can see, there is a long RUN command that does the job of installing NGINX from the official website. Now, this code snipped here, is not written by me. This is how NGINX is installed in the official image. You can check out their repository for clarification. What this command does is determine the desired package versions from the environment variable you set at the top of the file, then based on the OS version, installs the appropriate NGINX package.

The next RUN instruction links sdtout and stderr to NGINX access and error logs so that you can see the logs flowing on our terminal. The COPY instruction copies the docker/supervisord.conf to /etc/supervisord.conf, the default configuration file location for supervisor.

The code for the configuration file is as follows:

[supervisord]
user=root
nodaemon=true
logfile=/dev/stdout
logfile_maxbytes=0
pidfile=/var/run/supervisord.pid
loglevel = INFO

[program:php-fpm]
command = /usr/local/sbin/php-fpm
autostart=true
autorestart=true
priority=5
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0
stderr_logfile=/dev/stderr
stderr_logfile_maxbytes=0

[program:nginx]
command=/usr/sbin/nginx -g "daemon off;"
autostart=true
autorestart=true
priority=10
stdout_events_enabled=true
stderr_events_enabled=true
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0
stderr_logfile=/dev/stderr
stderr_logfile_maxbytes=0

In this configuration file, the supervisord section defines default configuration for supervisor. Then the two sections, program:php-fpm and program:nginx, daemonize PHP-FPM and NGINX to keep running side-by-side. If you want to learn more about configuring supervisor, feel free to consult the official documentation.

The EXPOSE instruction is like documentation for services that need to know which ports are accessible in any Laravel docker container created from this image. NGINX runs in port 80 by default and that’s why it’s exposed. Finally, the CMD instruction sets the default command of this image to ["/usr/bin/supervisord", "-n", "-c", "/etc/supervisord.conf"] or in other words, starts supervisor by on startup. Supervisor then starts NGINX and PHP-FPM and you get a functional server.

That’s pretty much it for the base image. I don’t like to include composer in the base image because composer is likely to be updated more frequently than PHP or NGINX. I prefer installing composer in my derived images or application images.

Anyways, if you’re happy with your base image, you can build, tag, and upload the image to your Docker Hub account as follows:

docker image build -t <your-docker-hub-username>/<your-base-image-name>:<your-base-image-tag> .

docker image push <your-docker-hub-username>/<your-base-image-name>:<your-base-image-tag>

I’m assuming that you already know how to build Docker images. If you need help, feel free to check out my handbook on the topic. If you’ve successfully built, tagged, and uploaded your image, advance to the next section.

Creating an Application Image

Now that you have a base image to use, let’s create an application image. The Dockerfile in my project contains the code for the application image. The code for the file is as follows:

FROM fhsinchy/php-nginx-base:php8.1.3-fpm-nginx1.20.2-alpine3.15

# set composer related environment variables
ENV PATH="/composer/vendor/bin:$PATH" \
    COMPOSER_ALLOW_SUPERUSER=1 \
    COMPOSER_VENDOR_DIR=/var/www/vendor \
    COMPOSER_HOME=/composer

# install composer
RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer \
    && composer --ansi --version --no-interaction

# install application dependencies
WORKDIR /var/www/app
COPY ./src/composer.json ./src/composer.lock* ./
RUN composer install --no-scripts --no-autoloader --ansi --no-interaction

# add custom php-fpm pool settings, these get written at entrypoint startup
ENV FPM_PM_MAX_CHILDREN=20 \
    FPM_PM_START_SERVERS=2 \
    FPM_PM_MIN_SPARE_SERVERS=1 \
    FPM_PM_MAX_SPARE_SERVERS=3

# set application environment variables
ENV APP_NAME="Question Board" \
    APP_ENV=production \
    APP_DEBUG=false

# copy entrypoint files
COPY ./docker/docker-php-* /usr/local/bin/
RUN dos2unix /usr/local/bin/docker-php-entrypoint
RUN dos2unix /usr/local/bin/docker-php-entrypoint-dev

# copy nginx configuration
COPY ./docker/nginx.conf /etc/nginx/nginx.conf
COPY ./docker/default.conf /etc/nginx/conf.d/default.conf

# copy application code
WORKDIR /var/www/app
COPY ./src .
RUN composer dump-autoload -o \
    && chown -R :www-data /var/www/app \
    && chmod -R 775 /var/www/app/storage /var/www/app/bootstrap/cache

EXPOSE 80

# run supervisor
CMD ["/usr/bin/supervisord", "-n", "-c", "/etc/supervisord.conf"]

As you can see, this image uses my base image as it’s, well, base. Make sure to replace my base image name and tag with yours. The first thing you need is composer. The ENV instruction sets some necessary environmental variable for composer.

Setting COMPOSER_ALLOW_SUPERUSER to 1 lets you run composer as superuser without any warning. Setting COMPOSER_VENDOR_DIR to /var/www/vendor allows you to keep the installed packages in the /var/www/vendor directory instead of a vendor directory within the project.

Some Laravel developers prefer using a bind mount to protect the vendor directory from getting overwritten but I prefer simply moving it out of the project directory. However, going this route will require you to update the artisan and public/index.php files in your project.

Open the artisan file and update the require __DIR__.'/vendor/autoload.php'; line as follows:

require __DIR__.'/../vendor/autoload.php';

By default, the vendor folder stays in the same directory as the artisan file. But now instead of /var/www/app/vendor it’s in the /var/www/vendor directory. Adding the additional .. will make artisan to go back one step in the directory tree. You have to make similar changes to the public/index.php file as well. Open the file and replace the require __DIR__.'/../vendor/autoload.php'; line as follow:

require __DIR__.'/../../vendor/autoload.php';

Just like artisan, you’ll need to add an extra set of dots before the vendor directory path. If you think these changes are not ideal, feel free to use a bind mount instead or leave the vendor directory as-is.

After setting the environment variables, the RUN instruction uses curl to download the official composer installer and installs it inside the /usr/local/bin directory with the filename composer. These installation instructions are taken directly from the official download instructions.

You can now use composer to install the application dependencies. To do so, you’ll have to first change the working directory to /var/www/app, using the WORKDIR instruction. You can use some other directory if you want. Then, you’ll have to use a COPY instruction to copy the ./src/composer.json ./src/composer.lock* files to the working directory. Since you’ve already configured the working directory, ./ refers to the working directory. Next, execute the composer install --no-scripts --no-autoloader --ansi --no-interaction command with a RUN instruction to install the dependencies. The --no-scripts option will instruct composer to not execute any post-installation scripts. The --no-autoloader option will prevent composer from generating the autoload file. Since we don’t have all the application code yet, generating the autoload at this point will not be ideal. The --ansi option instructs composer to produce ANSI output and --no-interaction means you want the package installation to be unattended.

Once the dependencies are installed, you may set any necessary environmental variables. I’ve set a bunch of environmental variables related to PHP-FPM. These variables will be later written to the PHP-FPM configuration. Feel free to get rid of them if you want to. Set any application related environment variable here but do not set anything sensitive like application encryption key or database password. They can be passed when starting the Laravel app container.

Now you’ll have to copy the entrypoint script. The dos2unix utility converts the CRLF line endings to LF line ending. If you’re confused about the entrypoint, you can learn more from here. If you’re using my project, the entrypoint scripts should be docker/docker-php-entrypoint and the code for this file is as follows:

#!/bin/sh
set -e

# write the php-fpm config
{ \
    echo listen = /var/run/php-fpm.sock; \
    echo listen.owner = www-data; \
    echo listen.group = www-data; \
    echo pm.max_children = "$FPM_PM_MAX_CHILDREN"; \
    echo pm.start_servers = "$FPM_PM_START_SERVERS"; \
    echo pm.min_spare_servers = "$FPM_PM_MIN_SPARE_SERVERS"; \
    echo pm.max_spare_servers = "$FPM_PM_MAX_SPARE_SERVERS"; \
} > /usr/local/etc/php-fpm.d/zzz-app.conf

exec "[email protected]"

This script writes the default configuration file for PHP-FPM. Setting listen to /var/run/php-fpm.sock; will instruct PHP-FPM to use the given UNIX socket for listening to requests. The listen.owner and listen.group options indicate the user and user group PHP-FPM should listen to. www-data user and user groups exist in any Linux distribution by default. You can skip the rest of the four configuration options if want to.

There’s another file docker/docker-php-entrypoint-dev but we’ll discuss that file later. The default location for the entrypoint script is /usr/local/bin/ so the COPY ./docker/docker-php-* /usr/local/bin/ will copy the entrypoint files and replace the default one.

Next, you’ll have to copy the NGINX configuration files. The docker/nginx.conf file is the default NGINX configuration file. The code for the file is as follows:

user  www-data;
worker_processes  auto;

error_log  /var/log/nginx/error.log notice;
pid        /var/run/nginx.pid;

events {
    worker_connections  1024;
}

http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;
    
    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';
    access_log  /var/log/nginx/access.log  main;
    
    sendfile        on;
    #tcp_nopush     on;
    
    keepalive_timeout  65;
    
    #gzip  on;
    
    include /etc/nginx/conf.d/*.conf;
}

This is more or less entirely the same as the default NGINX configuration file. The only change I’ve made is in the first line. By default, it says user nginx; but you want it to be user www-data;. Next, the docker/default.conf file is the configuration for the application itself. The code for this file is as follows:

server {
    listen 80;
    
    root /var/www/app/public;
 
    add_header X-Frame-Options "SAMEORIGIN";
    add_header X-Content-Type-Options "nosniff";
 
    index index.php;
 
    charset utf-8;
 
    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }
 
    location = /favicon.ico { access_log off; log_not_found off; }
    location = /robots.txt  { access_log off; log_not_found off; }
 
    error_page 404 /index.php;
 
    location ~ \.php$ {
        fastcgi_pass unix:/var/run/php-fpm.sock;
        fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
        include fastcgi_params;
    }
    location ~ /\.(?!well-known).* {
        deny all;
    }
}

This code is copied from the official Laravel documentation. Make sure to change the root /var/www/app/public; line as per your directory. Also, make sure fastcgi_pass unix:/var/run/php-fpm.sock``; line matches the socket file location according to your entrypoint script.

The default location for the nginx.conf file inside the image should be /etc/nginx/nginx.conf and the default.conf should be /etc/nginx/conf.d/default.conf and that’s what the COPY instruction does.

Finally, you can copy the application code. As you can see in the code, I’ve set the working directory once again. This is to make sure that the working directory is still what it should be and has not been overwritten. The COPY instruction copies everything from the src directory into the working directory.

Then the RUN instruction executes the composer dump-autoload -o command to generate the autoload file. Then the chown -R :www-data /var/www/app command changes the directory owner group to www-data and the chmod -R 775 /var/www/app/storage /var/www/app/bootstrap/cache makes the /var/www/app/storage, /var/www/app/bootstrap/cache directories writable. These permission changes are necessary and you can learn more from this Guide to Deploying Laravel Applications on Virtual Private Servers. You should copy the application code as late as possible because it’s most likely to change.

After that, you just EXPOSE the port 80 and set ["/usr/bin/supervisord", "-n", "-c", "/etc/supervisord.conf"] as the default command. That’s pretty much it. Build the image and try to spin a new container to make sure everything’s working fine.

Creating The docker-compose.yaml File

Your project is now containerized. The image you built in the previous section is production-ready. The only thing that’s left to do is write a docker-compose.yaml file for development. Open the docker-compose.yaml file and the code for this file is as follows:

version: "3.8"

services: 
    db:
        image: mysql:8.0
        volumes: 
            - db-data:/var/lib/mysql
        environment:
            - MYSQL_ROOT_PASSWORD=root
            - MYSQL_DATABASE=question_board
    app:
        build: .
        entrypoint: /usr/local/bin/docker-php-entrypoint-dev
        command: ["/usr/bin/supervisord", "-n", "-c", "/etc/supervisord.conf"]
        volumes:
            - ./src:/var/www/app
        environment: 
            - APP_ENV=local
            - APP_KEY=base64:tLmYfUrrZITzLIkSjFnV+PCAFxkdU+duUxjVSIlrrHo=
            - APP_DEBUG=true
            - DB_CONNECTION=mysql
            - DB_HOST=db ## same as the database service name
            - DB_PORT=3306
            - DB_DATABASE=question_board
            - DB_USERNAME=root
            - DB_PASSWORD=root
        restart: unless-stopped
        ports: 
            - 8000:80
        depends_on:
            - db
            
volumes:
    db-data:

This is a regular docker-compose.yaml file that you may have written in the past. There are two services. The db service and the app service. The db service uses the mysql:8.0 image to create a new database container. It uses the db-data named volume for persisting data. Since the volume is named, it’ll hang around between ups.

The app service uses the Dockerfile to build an image and start a container. The entrypoint: /usr/local/bin/docker-php-entrypoint-dev line will override the default entrypoint script with the development entrypoint script. The code for this script is as follows:

#!/bin/sh
set -e

# run last minute build tools just for local dev
# this file should just be used to override on local dev in a compose file

# run default entrypoint first
/usr/local/bin/docker-php-entrypoint

# ensure bind mount permissions are what we need
chown -R :www-data /var/www/app

chmod -R 775 /var/www/app/storage /var/www/app/bootstrap/cache
  
# run last minute build tools just for local dev
cd /var/www/app
composer dump-autoload
cd /var/www/app/public

exec "[email protected]"

This script runs the default entrypoint script first. Then it resets the directory permissions because in development, you’ll mount your local src directory inside the container and it may mess up the permissions. After that, the script just regenerates the autoload. Since you’ve overridden the entrypoint, you’ll have to write the command once again.

The rest of the code mounts the src directory to the /var/www/app directory, sets the environment variables, exposes port 80 from inside the container to port 8000 on your host system. That’s it. You can use this file to start the containers in development mode.

There is a file Makefile that contains the following code:

stop:
    docker-compose stop
shell:
    docker-compose exec app sh
start:
    docker-compose up --detach
destroy:
    docker-compose down --volumes
build:
    docker-compose up --detach --build
seed:
    docker-compose exec app php artisan db:seed
migrate:
    docker-compose exec app php artisan migrate:fresh

You can use any of these commands like make start to execute the attached docker-compose command. Make may not be available on Windows but you can always use WSL for that.

Conclusion

I would like to thank you for the time you’ve spent reading this article.

I hope you’ve enjoyed it and have learned some useful stuff regarding PHP, Laravel, and Docker.

If you want to learn Docker from the ground up, consider checking out my open-source Docker Handbook with tons of fun content and several practical projects.
Or, if you are more interested about node.js, here is an article on how to do containerizing Node.js applications with Docker.

Laravel News Links

Make pivot tables using the new Laravel 9 migration closures

https://opengraph.githubassets.com/e6160907e12387e0dfc32f03979df89b1707fa8fb48f4fc8f4ebc613d2e29ed8/josezenem/laravel-make-migration-pivot

Make Laravel Pivot Tables using the new Laravel 9 closure migration format

Latest Version on Packagist
GitHub Code Style Action Status
Total Downloads

This will allow you to create pivot table migration files using the new Laravel 9 closure migration format by simply passing two models. Under the hood the system will inspect the two models to generate the pivot table and foreign key names.

php artisan make:pivot Category Blog

Will generate the following migration

return new class extends Migration {
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('blog_category', function (Blueprint $table) {
            $table->foreignIdFor(Blog::class)->constrained()->onDelete('cascade');
            $table->foreignIdFor(Category::class)->constrained()->onDelete('cascade');
            $table->primary(['blog_id', 'category_id']);

            $table->index('blog_id');
            $table->index('category_id');
        });
    }

Installation

You can install the package via composer:

composer require josezenem/laravel-make-migration-pivot

Usage

php artisan make:pivot Category Blog

Optionally, you can publish the stubs using

php artisan vendor:publish --tag="laravel-make-migration-pivot-stubs"

Testing

Changelog

Please see CHANGELOG for more information on what has changed recently.

Contributing

Please see CONTRIBUTING for details.

Security Vulnerabilities

Please review our security policy on how to report security vulnerabilities.

Credits

License

The MIT License (MIT). Please see License File for more information.

Laravel News Links