Argon2 password hashing is one of those security upgrades I ignored for longer than I should have. I’ve been hashing passwords with bcrypt since I started coding. Like, I learned it and just… kept using it. It worked. It was secure. Everyone recommended it. Why fix what isn’t broken, right?
If you want the background first, I also wrote a separate guide to bcrypt password hashing and why developers have trusted it for so long.
Then last month I was reviewing some security documentation and saw OWASP’s latest recommendation: use Argon2. Not bcrypt. Argon2. And I realized I’d been ignoring this because bcrypt was “good enough.”
But here’s the thing about security: “good enough” stops being good enough the moment attackers get better tools. And attackers have way better tools than they did in five or ten years ago.
So I finally did a deep dive on Argon2, implemented it in a couple of projects, and now I’m here to tell you why you should probably switch too. Or at least understand what you’re missing by sticking with the old guard.
What Is Argon2 Password Hashing?
Argon2 is a password hashing algorithm that won the Password Hashing Competition in 2015. Yeah, there was literally a competition to find the best password hashing algorithm. Security people take this stuff seriously.
It was designed specifically to resist modern cracking techniques, GPUs, ASICs, side-channel attacks, all the stuff that makes older algorithms sweat. The team that built it analyzed every known attack vector and built defenses against them.
There are actually three versions:
- Argon2d: Maximizes resistance to GPU cracking but vulnerable to side-channel attacks
- Argon2i: Maximizes resistance to side-channel attacks but slightly weaker against GPUs
- Argon2id: A hybrid that combines both, and this is what you should use
When I say “use Argon2,” I mean Argon2id. That’s the recommended variant for password hashing.
Why Password Hashing Is Actually Hard
Quick primer because not everyone thinks about this: you don’t store passwords in plain text. Ever. When someone creates an account, you hash their password and store the hash. When they log in, you hash what they typed and compare it to the stored hash.
The key is making that hash expensive to compute. Because if someone steals your database (and let’s be real, breaches happen), they’re going to try to crack those hashes by guessing millions of passwords and hashing each one to see if it matches.
The algorithm needs to be:
- Slow enough that brute-force attacks take forever
- Fast enough that legitimate users don’t wait long to log in
- Memory-hard so attackers can’t just throw GPUs at it
- Resistant to side-channel attacks so attackers can’t learn secrets by measuring timing or power consumption
Getting all of this right is way harder than it sounds.
What Makes Argon2 Password Hashing Different?
Argon2’s secret sauce is that it’s extremely memory-hard. It doesn’t just take time to compute, it requires a configurable amount of RAM. And you get to tune three parameters:
Memory cost (m): How much RAM to use per hash (in KB) Time cost (t): Number of iterations to run Parallelism (p): Number of threads to use
The memory requirement is what makes it special. With bcrypt, you can run millions of parallel computations on a GPU because each one only needs about 4KB of RAM. With Argon2, if you configure it to use 64MB per hash, suddenly that GPU can only run a handful in parallel. The economics of cracking change completely.
I tested this on my own machine. Configured Argon2 to use 64MB of memory, and suddenly my GPU that could theoretically run thousands of bcrypt hashes in parallel could only handle a few dozen Argon2 hashes. That’s a massive difference in attack economics.
The Parameters Thing (Both Blessing and Curse)
Here’s where Argon2 gets a bit annoying: you have to actually think about configuration.
With bcrypt, you pick a cost factor (usually 12 or 13) and you’re done. With Argon2, you have three knobs to turn, and the optimal settings depend on your hardware and threat model.
The recommended settings I’m using:
Memory: 65536 KB (64 MB)
Iterations: 3
Parallelism: 4This gives me about 150-200ms to hash a password on my server, which feels right. Fast enough for users, slow enough for attackers.
But you need to benchmark this on your actual hardware. A cheap VPS will struggle with 64MB per hash if you get a login spike. A beefy server can handle way more. There’s no one-size-fits-all answer.
This configurability is both Argon2’s strength and its weakness. You can tune it perfectly for your needs, but you can also misconfigure it and end up with something weaker than bcrypt.
How I Actually Implemented This
Let me show you what this looks like in practice, because the theory is only useful if you can actually use it.
Python:
from argon2 import PasswordHasher
ph = PasswordHasher(
time_cost=3,
memory_cost=65536,
parallelism=4
)
# Hashing a password
hash = ph.hash("user_password_here")
# Verifying a password
try:
ph.verify(hash, "user_password_here")
print("Password correct!")
except:
print("Wrong password")The argon2-cffi library is excellent. It’s a Python wrapper around the reference C implementation, so it’s both fast and well-tested.
Node.js:
const argon2 = require('argon2');
// Hashing
const hash = await argon2.hash('user_password_here', {
type: argon2.argon2id,
memoryCost: 65536,
timeCost: 3,
parallelism: 4
});
// Verifying
try {
if (await argon2.verify(hash, 'user_password_here')) {
console.log('Password correct!');
}
} catch {
console.log('Wrong password');
}The Node implementation is solid too. It’s what I’m using in production now.
Go:
import "golang.org/x/crypto/argon2"
hash := argon2.IDKey(
[]byte("user_password_here"),
salt,
3, // time
64*1024, // memory in KB
4, // threads
32, // key length
)Go’s implementation requires a bit more manual work (you handle the salt yourself), but it’s part of the official crypto library which is nice.
The Migration Problem (aka My Current Headache)
So you’re convinced Argon2 is better. Great. Now what about your existing database with 100,000 bcrypt hashes?
You can’t just convert them. Hashing is one-way. You don’t have the original passwords.
The standard migration approach:
- Add a
hash_typecolumn to your users table - Keep your bcrypt verification code working
- When users log in successfully, re-hash with Argon2 and update both the hash and the type
- Eventually, most active users migrate
It’s fine but it means maintaining two code paths for potentially years. And users who haven’t logged in don’t get migrated, which is bothering from a security perfectionism standpoint.
Some people force a password reset for everyone. But keep in mind that most users hate mandatory password resets and you might get support tickets and angry emails.
For new projects, just start with Argon2 and avoid this whole mess.
Real-World Performance Considerations
Theory is great but how does this actually perform in production?
When migrating from bcrypt to Argon2, several practical considerations emerge:
Memory usage increases significantly. Argon2 at 64MB per hash demands higher server RAM allocation compared to bcrypt’s minimal memory footprint. During high-traffic periods, this can create scaling pressures.
Cost impact is measurable. The increased resource consumption translates to real infrastructure expenses, though this must be weighed against the security improvements.
CPU usage remains comparable. Argon2 with standard settings uses similar CPU time to bcrypt at cost 12, so computational overhead isn’t a major differentiator.
Login times show minimal difference. The gap between typical bcrypt (160ms) and Argon2 (180ms) hashing falls below user perception thresholds.
Configuration complexity increases slightly. Argon2 requires more parameter tuning and testing compared to bcrypt’s simpler setup, adding some implementation overhead.
When You Shouldn’t Use Argon2
I’m bullish on Argon2 but there are cases where it might not be the right choice:
Memory-constrained environments. If you’re running on a $5/month VPS with 512MB of RAM, Argon2 configured properly might cause problems. You could use lower memory settings, but at some point you’re better off with bcrypt.
Legacy systems. If you’re on ancient infrastructure where getting Argon2 libraries is difficult, forcing it might not be worth the hassle. Security is important but so is shipping.
You’re already using bcrypt correctly. If you’ve got bcrypt with a proper cost factor (12+) and your threat model doesn’t include nation-state attackers with custom ASICs, you’re probably fine. Don’t let perfect be the enemy of good.
Super high-traffic login systems. If you’re doing tens of thousands of logins per second, the memory requirements of Argon2 might be prohibitive. Though at that scale, you have bigger problems and should probably hire security engineers who know way more than me.
The Side-Channel Attack Thing
One thing Argon2i (and by extension Argon2id) protects against is side-channel attacks. This is where attackers don’t crack the hash directly. They measure timing differences or power consumption or cache access patterns to learn information about the password.
Is this a real threat for most apps? Probably not. You need physical access or very specific conditions to pull off these attacks.
But the fact that Argon2 protects against them by default is nice. It’s the kind of forward-thinking security that might matter in five years even if it doesn’t today.
bcrypt is vulnerable to cache-timing attacks in some implementations. Argon2i specifically defends against this by using data-independent memory access patterns.
For most web apps, this doesn’t matter. But if you’re building something that might be a target for sophisticated attacks (think: cryptocurrency exchanges, government systems, healthcare), this extra protection is valuable.
What the Experts Actually Say
OWASP (the Open Web Application Security Project) now recommends Argon2id as the first choice for password hashing. bcrypt is listed as acceptable but not preferred.
NIST (the US standards body) also recommends Argon2.
libsodium (the cryptography library everyone trusts) uses Argon2 internally for its password hashing functions.
This isn’t just me saying it’s better. The security community has reached consensus that Argon2 is the modern standard.
Does that mean bcrypt is broken? No. It means Argon2 is better, and when you’re making security decisions for new systems, you should use the best available tool.
My Honest Take After Using Both
When evaluating Argon2 against bcrypt in production environments, several factors emerge:
Setup complexity increases moderately. Argon2 requires parameter configuration rather than simple cost factor adjustment. The learning curve is manageable and represents a one-time investment.
Memory requirements create tangible infrastructure costs. Higher server resource allocation becomes necessary, though the impact scales with project size, significant for smaller systems but negligible for larger deployments.
Migration overhead is present but manageable. Running dual authentication paths requires maintenance effort, though this burden decreases as users transition over time.
Security improvements are substantive. GPU-based attack resistance provides meaningful protection advantages that justify consideration in threat modeling.
User experience remains unchanged. Login performance shows no perceptible difference, and the switch generates no user-facing disruption.
Implementation decisions depend on context. New projects benefit from Argon2 adoption. Migration of existing stable systems should align with specific threat models and available resources.
Practical Advice for Switching to Argon2 Password Hashing
If you’re actually going to implement Argon2, here’s what you should keep in mind:
1. Benchmark on your actual hardware. Don’t just use the recommended settings. Test them under load on your production servers.
2. Start with conservative settings. You can always increase security later. But if you set the memory too high and your server OOMs during a login spike, that’s bad.
3. Monitor memory usage closely. Set up alerts so you know if you’re getting close to limits.
4. Document your parameters. Future you (or future developers) will need to know why you chose those specific settings.
5. Plan the migration carefully. Don’t just flip a switch. Gradual migration with good monitoring is the way.
6. Use established libraries. Don’t implement Argon2 yourself. Use the reference implementation or well-maintained wrappers.
The Bottom Line
Argon2 is the modern standard for password hashing. It’s better than bcrypt in meaningful ways, memory-hardness, GPU resistance, side-channel protection, configurability.
Should you drop everything and migrate? Probably not. Should you use it for new projects? Yes, definitely.
Security is about using the best available tools for the job. Argon2 won a competition specifically designed to find the best password hashing algorithm. The experts agree it’s the right choice. The libraries are mature and well-tested.
If you’re still using bcrypt, you’re not wrong. But you’re not using the best tool available anymore, and that’s worth thinking about. For new applications, Argon2 password hashing is the password storage approach I’d choose by default.
Now if you’ll excuse me, I need to go buy more RAM for my production servers.

